I have a UnityEditor script where I have long running processes (File.ReadAllText() and XMLNode.SelectSingleNode()) that start when a button is clicked. I just want to inform the user to be patient. The problem is that the time consuming processing starts immediately when the button is clicked. The GUI doesn't do a redraw where I can put my message to the user.
I currently solve it for me by simply running a coroutine multiple times to be sure the message is drawn. But that looks cumbersome to me. Does anyone know better?
Thank you awesome people from SO :)
int fileProcessingEnumerator = 0;
// called on every redraw of my editor
void OnGUI()
{
DisplayRunButton();
}
/// <summary>
/// Draw a GUI button and handle the click
/// </summary>
private void DisplayRunButton()
{
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("<< GO! >>"))
{
fileProcessingEnumerator = 1;
EditorCoroutineUtility.StartCoroutine(ProcessFiles(), this);
}
if (fileProcessingEnumerator > 0)
EditorGUILayout.LabelField("Importprocess is running, please be patient..");
EditorGUILayout.EndHorizontal();
}
/// <summary>
/// Do the import of the files
/// </summary>
private IEnumerator ProcessFiles()
{
fileProcessingEnumerator++;
if (fileProcessingEnumerator < 3)
yield return null;
apDocument = new ApDocument(File.ReadAllText(apFilePath));
cDocument = new CDocument(File.ReadAllText(cFilePath)); //very expensive function
fileProcessingEnumerator = 0;
}
So as said you should make the loading asynchronous. Note that a Coroutine, in playmode or in the Editor, is never asynchronous. All that it allows is to yield which basically does
"Pause this routine, render this frame, continue this routine from here in the frame"
Everything between two yield statements is still executed in the Unity main thread.
Now there are probably many valid ways of how to do that exactly.
One would be to use an async Task and a main thread dispatcher pattern. Have to say that I'm not an expert for async - await so not totally sure it is done that way or if there is a more elegant solution ;)
// These are thread safe queues for handling loaded files back in the Unity thread
private readonly ConcurrentQueue<ApDocument> apDocs = new ConcurrentQueue<ApDocuemnt>();
private readonly ConcurrentQueue<cDocument> cDocs = new ConcurrentQueue<cDocuemnt>();
// Check if the routine is running
private bool busy;
// called on every redraw of my editor
void OnGUI()
{
DisplayRunButton();
}
/// <summary>
/// Draw a GUI button and handle the click
/// </summary>
private void DisplayRunButton()
{
EditorGUILayout.BeginHorizontal();
{
// Disable the button during the loading
EditorGUI.BeginDisabledGroup(busy);
{
if (GUILayout.Button("<< GO! >>"))
{
EditorCoroutineUtility.StartCoroutine(ProcessFiles(), this);
}
}
EditorGUI.EndDisabledGroup();
if(busy)
{
EditorGUILayout.LabelField("Importprocess is running, please be patient..");
}
}
EditorGUILayout.EndHorizontal();
}
private IEnumerator ProcessFilesRoutine()
{
// Set the busy flag for the drawer
busy = true;
// Start the async task
var loadingTask = Task.Run(ProcessFiles);
// Wait until the async task is done without freezing
yield return new WaitUntil(() => loadingTask.IsCompleted);
// handle the results
while(apDocs.TryDequeue(out var apDoc))
{
apDocument = apDoc;
}
while(cDocs.TryDequeue(out var cDoc))
{
cDocument = cDoc;
}
// Release the flag
busy = false;
}
// Do long stuff in a Task using async - await
private async Task ProcessFiles()
{
// Async file reading
var apContent = await File.ReadAllTextAsync(apFilePath);
var apDoc = new ApDocument(apContent);
var cContent = await File.ReadAllText(cFilePath);
var cDoc = new CDocument();
// Enqueue the results into the thread safe queuea
apDocs.Enqueue(apDoc);
cDocs.Enqueu(cDoc);
}
Note: Typed on smartphone but I hope the idea gets clear
Related
I am trying to write a chess program in C# Windows Forms and I am writing a method GetMove() in this HumanPlayer class I have, which will return the Move from a player input of two clicks on separate squares on the board UI.
Could I have some help / advice on what I should use to implement this or if I am misunderstanding something else, explain that to me.
I've tried to add code snippets, but please let me know if I've done them wrong.
class HumanPlayer : Player
{
private Coords _selected;
public HumanPlayer(PieceColour colour) : base(colour)
{
_selected = new Coords();
}
public override ChessMove GetMove(Board board)
{
Coords Start = new Coords();
board.RaiseSquareClicked += ReceiveStartSquareClickInfo;
// Want to wait until that function is triggered by the event until continuing
Start = _selected;
board.RaiseSquareClicked -= ReceiveStartSquareClickInfo;
Coords End = new Coords();
board.RaiseSquareClicked += ReceiveEndSquareClickInfo;
// Want to wait until that function is triggered by the event until continuing
End = _selected;
board.RaiseSquareClicked -= ReceiveEndSquareClickInfo;
return new ChessMove(Start, End);
}
public void ReceiveStartSquareClickInfo(object sender, SquareClickedEventArgs e)
{
_selected = e.Square.Coords;
}
public void ReceiveEndSquareClickInfo(object sender, SquareClickedEventArgs e)
{
_selected = e.Square.Coords;
}
}
One thing I tried was using AutoResetEvent and WaitOne() and Set(), but this caused the UI to stop displaying.
I also tried to understand and use await and async, but I just confused myself and overcomplicated it and didn't get anywhere with it. So it might just be that I need someone to explain it to me.
This code that doesn't work might help someone understand what I misunderstand about asynchronous functions etc.
public async void Play()
{
_currentPlayer = _players[0];
_currentTurn = 1;
while (!GameOver())
{
ChessMove move= await _currentPlayer.GetMove(_board);
if (_currentPlayer == _players[1])
{
_currentTurn += 1;
_currentPlayer = _players[0];
}
else
{
_currentPlayer = _players[1];
}
}
}
class HumanPlayer : Player
{
private Coords _selected;
private TaskCompletionSource<bool> _squareClicked;
public HumanPlayer(PieceColour colour) : base(colour)
{
_selected = new Coords();
}
public override async Task<ChessMove> GetMove(Board board)
{
Coords Start = new Coords();
_squareClicked = new TaskCompletionSource<bool>();
board.RaiseSquareClicked += ReceiveStartSquareClickInfo;
_squareClicked.Task.Wait();
Start = _selected;
board.RaiseSquareClicked -= ReceiveStartSquareClickInfo;
Coords End = new Coords();
_squareClicked = new TaskCompletionSource<bool>();
board.RaiseSquareClicked += ReceiveEndSquareClickInfo;
_squareClicked.Task.Wait();
End = _selected;
board.RaiseSquareClicked -= ReceiveEndSquareClickInfo;
return new ChessMove(Start, End);
}
public async void ReceiveStartSquareClickInfo(object sender, SquareClickedEventArgs e)
{
_squareClicked.SetResult(true);
_selected = e.Square.Coords;
}
public async void ReceiveEndSquareClickInfo(object sender, SquareClickedEventArgs e)
{
_squareClicked.SetResult(true);
_selected = e.Square.Coords;
}
}
I've kinda been hesitant / nervous to post this question because I don't want people to get annoyed at me for posting a "duplicate question". Even though I've looked through several of the questions, it is confusing and frustrating not knowing whether the solution just doesn't apply to my situation or if I've added it in wrong. I'm sure I could find my solution answered in another question, but I feel it would take me a lot longer to find it and understand it than posting my own question.
Sorry if I've posted this question wrong or not followed the guidelines, this is my first post here.
If I've done anything wrong in formatting / communicating through this post, let me know and I'll try to fix it.
Good question, from what I can understand, your wanting to wait on a response from another method before executing yours.
Using async/await is the best option for this, if your getting confused at that, there are some tutorials and such you can follow
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/await
https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/
https://dotnettutorials.net/lesson/async-and-await-operator-in-csharp/
TaskCompletionSource is a class in C# that enables creating a Task object which can be manually completed with a result or exception.
class Example
{
TaskCompletionSource<string> taskCompletionSource = new();
public async Task DoStuff()
{
// Wait for the result of the TaskCompletionSource
var result = await taskCompletionSource.Task;
Console.WriteLine(result);
}
public void SetResult(){
taskCompletionSource.SetResult("Hello World!");
}
}
In this example, calling to DoStuff method will wait until the SetResult method is called, which will then set the result variable to "Hello World!"
Your post states that you want to wait for another method to be triggered before continuing and then describes three "states of play" so my first suggestion is to identify in code exactly the things we need to wait for in the chess game loop.
enum StateOfPlay
{
PlayerChooseFrom,
PlayerChooseTo,
OpponentTurn,
}
Game Loop
The goal is to run a loop that cycles these three states continuously, waiting at each step. However, the main Form is always running its own Message Loop to detect mouse clicks and key presses and it's important not to block that loop with our own.
The await keyword causes a waiting method to return immediately which allows the UI loop to keep running. But when "something happens" that we're waiting for, the execution of this method will resume on the next line after the await. A semaphore object says when to stop or go and is initialized here in the waiting state.
SemaphoreSlim _semaphoreClick= new SemaphoreSlim(0, 1);
When the game board is clicked during the players turn then the Release() method will be called on the semaphore, allowing things to resume. In terms of the specific question that you asked, this code snippet shows how to use the await keyword in your chess game loop.
private async Task playGameAsync(PlayerColor playerColor)
{
StateOfPlay =
playerColor.Equals(PlayerColor.White) ?
StateOfPlay.PlayerChooseFrom :
StateOfPlay.OpponentTurn;
while(!_checkmate)
{
switch (StateOfPlay)
{
case StateOfPlay.PlayerChooseFrom:
await _semaphoreClick.WaitAsync();
StateOfPlay = StateOfPlay.PlayerChooseTo;
break;
case StateOfPlay.PlayerChooseTo:
await _semaphoreClick.WaitAsync();
StateOfPlay = StateOfPlay.OpponentTurn;
break;
case StateOfPlay.OpponentTurn:
await opponentMove();
StateOfPlay = StateOfPlay.PlayerChooseFrom;
break;
}
}
}
Player's turn
Here we have to wait for each square to get clicked. A straightforward way to do this is with a SemaphoreSlim object and call Release() when the game board is clicked during the player's turn.
Square _playerFrom, _playerTo, _opponentFrom, _opponentTo;
private void onSquareClicked(object sender, EventArgs e)
{
if (sender is Square square)
{
switch (StateOfPlay)
{
case StateOfPlay.OpponentTurn:
// Disabled for opponent turn
return;
case StateOfPlay.PlayerChooseFrom:
_playerFrom = square;
Text = $"Player {_playerFrom.Notation} : _";
break;
case StateOfPlay.PlayerChooseTo:
_playerTo = square;
Text = $"Player {_playerFrom.Notation} : {_playerTo.Notation}";
richTextBox.SelectionColor = Color.DarkGreen;
richTextBox.AppendText($"{_playerFrom.Notation} : {_playerTo.Notation}{Environment.NewLine}");
break;
}
_semaphoreClick.Release();
}
}
Opponents turn
This simulates a computer opponent processing an algorithm to determine its next move.
private async Task opponentMove()
{
Text = "Opponent thinking";
for (int i = 0; i < _rando.Next(5, 10); i++)
{
Text += ".";
await Task.Delay(1000);
}
string opponentMove = "xx : xx";
Text = $"Opponent Moved {opponentMove}";
richTextBox.SelectionColor = Color.DarkBlue;
richTextBox.AppendText($"{opponentMove}{Environment.NewLine}");
}
It might be helpful to look at another answer I wrote that describes how to create the game board with a TableLayoutPanel and how to interact with mouse to determine the square that's being clicked on.
Problem
The problem can arise in multiple common scenarios:
You press a button and want to be sure if the button handler logic is only executed once while the button handler is running. As soon as the button handler has finished, the button can be pressed again and will start the button handler logic again. The button handler logic must never be executed concurrently.
Imagine you have to send a bitmap image to a very slow display (e.g. a waveshare eink display which has a refresh rate of 2s per full refresh cycle) and you have hardware push buttons which cause the display to show a new bitmap. You can press buttons in a mich faster rate than the display will be able to refresh. For this purpose, I need a locking around the refresh handler of the display. I want to avoid concurrent calls to the display refresh routine if the user presses the pus buttons in faster rate than the display can process the images.
Attempt 1: Using SemaphoreSlim
My first attempt looks like follows: I'm using a SemaphoreSlim for which I created an extension method "ExecuteOnceAsync" which has a parameter "action" (which is essentially a task creator factory for the task we only want to run once).
public static class SemaphoreSlimExtensions
{
/// <summary>
/// Executes a task within the context of a a SemaphoreSlim.
/// The task is started only if no <paramref name="action"/> is currently running.
/// </summary>
/// <param name="semaphoreSlim">The semaphore instance.</param>
/// <param name="action">The function to execute as a task.</param>
public static async Task ExecuteOnceAsync(this SemaphoreSlim semaphoreSlim, Func<Task> action)
{
if (semaphoreSlim.CurrentCount == 0)
{
return;
}
try
{
await semaphoreSlim.WaitAsync();
await action();
}
finally
{
try
{
semaphoreSlim.Release();
}
catch (SemaphoreFullException)
{
// Ignored
}
}
}
}
Following unit test should show a sample usage where a call to a task with a shared resource is done 100 times in parallel but we want to make sure it is only executed once:
[Fact]
public async Task ShouldExecuteOnceAsync()
{
// Arrange
var counter = 0;
var parallelTasks = 100;
var semaphoreSlim = new SemaphoreSlim(1, 1);
Func<Task> action = () => Task.Run(() =>
{
counter++;
this.testOutputHelper.WriteLine($"Run: counter={counter}");
return counter;
});
// Act
var tasks = Enumerable.Range(1, parallelTasks).Select(i => semaphoreSlim.ExecuteOnceAsync(action));
await Task.WhenAll(tasks);
// Assert
counter.Should().Be(1);
}
This solution is not thread-safe since CurrentCount is not implemented thread-safe. So, forget about this attempt!
Attempt 2: Using AsyncLazy
AsyncLazy allows to run initialization code once and in a thread-safe manner.
The problem with AsyncLazy is that it will only run once per instance of AsyncLazy. My button clicks will occur multiple times. If one handler finishes, another button handler must be run. They must just not run simultaneously.
Attempt 3: Using Interlocked.CompareExchange
With the help of other Stackoverflow questions and some github searches, I was able to put following code together. This code atomically sets a flag (currentState) to either 0 (NotRunning) or 1 (Running). CompareExchange checks and updates the currentState. The finally section ensures that the flag is reset from Running to NotRunning after the task is finished.
internal class SyncHelper
{
private const int NotRunning = 0;
private const int Running = 1;
private int currentState;
public async Task RunOnceAsync(Func<Task> task)
{
if (Interlocked.CompareExchange(ref this.currentState, Running, NotRunning) == NotRunning)
{
// The given task is only executed if we pass this atomic CompareExchange call,
// which switches the current state flag from 'not running' to 'running'.
var id = $"{Guid.NewGuid():N}".Substring(0, 5).ToUpperInvariant();
Debug.WriteLine($"RunOnceAsync: Task {id} started");
try
{
await task();
}
finally
{
Debug.WriteLine($"RunOnceAsync: Task {id} finished");
Interlocked.Exchange(ref this.currentState, NotRunning);
}
}
// All other method calls which can't make it into the critical section
// are just returned immediately.
}
}
The code above has a race condition, between checking the CurrentCount of the semaphore and calling WaitAsync.
A better way would be to use semaphoreSlim.WaitAsync(0):
public static async Task ExecuteOnceAsync(this SemaphoreSlim semaphoreSlim,
Func<Task> action)
{
try
{
if (! await semaphoreSlim.WaitAsync(0)) return;
await action();
}
finally
{
try
{
semaphoreSlim.Release();
}
catch (SemaphoreFullException)
{
// Ignored
}
}
}
Your test may not help. There is no reliable way to test concurrent code, because anything can happen, and therefore the test is not idempotent.
I have a Winforms application where I am trying to print a pdf document which has multiple layers on it.
But the problem is, This all operation are running on UI thread and it is hanging the UI(not responding) for long time.
I know, this is happening because of UI thread is blocked so, I have tried to make this operation asynchronous by the help of powerful async/await keyword but still my long running method is not being asynchronous. It is not coming forward from the await tasks and still opearation is taking the same time as like synchronous operation.
What I tried:
Please see below:
/// <summary>
/// Show Print Dialog
/// </summary>
private void ShowPrintDialog()
{
// Initialize print dialog
System.Windows.Controls.PrintDialog prtDialog = new System.Windows.Controls.PrintDialog();
prtDialog.PageRangeSelection = PageRangeSelection.AllPages;
prtDialog.UserPageRangeEnabled = false;
_printOptions.PrintQueue = null;
_printOptions.PrintTicket = null;
Enabled = false;
// if there is a default printer then set it
string defaulPrinter = prtDialog.PrintQueue == null ? string.Empty : prtDialog.PrintQueue.FullName;
// Display the dialog. This returns true if the user selects the Print button.
if (prtDialog.ShowDialog() == true)
{
_printOptions.PrintQueue = prtDialog.PrintQueue;
_printOptions.PrintTicket = prtDialog.PrintTicket;
_printOptions.UseDefaultPrinter = (defaulPrinter == prtDialog.PrintQueue.FullName);
}
// Re-enable the form
Enabled = true;
}
/// <summary>
/// Event raised when user clicks Print
/// </summary>
/// <param name="sender">Source of the event</param>
/// <param name="e">Event specific arguments</param>
private void cmdOk_Click(object sender, EventArgs e)
{
ShowPrintDialog();
if (_printOptions.PrintTicket != null)
{
//Set search Options
_print.ExportDataItem = true;
_print.FileName = SearchTemplateName;
//shows progress bar form.
using (frmPrintSearchResultsProgress frmProgress =
new frmPrintSearchResultsProgress(_print, this, _printOptions))
{
frmProgress.ShowDialog(this);
}
if (_print.ExportDataItem && !_print.DataItemExported && !_print.CancelExport)
{
MessageBox.Show("No Document printed.");
}
}
//Store selected options for current user
SaveOptions();
if (!SkipExport)
Close();
}
/// <summary>
/// Event raised when progress form is shown.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void frmExportSearchResultsProgress_Shown(object sender, EventArgs e)
{
try
{
Application.DoEvents();
dispatcher = Dispatcher.CurrentDispatcher;
// record export/print job start time
_startedUtc = DateTime.UtcNow;
_print.WritingToPdfIndicator = lblWritingPdfFile;
lblProgress.Text = Properties.Resources.PrintSearchResults;
await dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(DoDataItemPrint));
}
}
/// <summary>
/// Prints the selected data items.
/// </summary>
private void DoDataItemPrint()
{
// LONG RUNNING OPERATIONS..
// THIS OPERATION IS BLOCKING THE UI.
}
So, as per mentioned in above code when I opened the PringDialogForm then it is opening a Progress Bar form to see the progress of printing the document and from here frmExportSearchResultsProgress_Shown() event is fired and inside it, I am calling the DoDataItemPrint() method which is time consuming.
So, I tried to make frmExportSearchResultsProgress_Shown event as async/await but still operation is taking the same time as previous.
Can anyone please suggest me where I am doing wrong?
your frmExportSearchResultsProgress_Shown method starts on the UI thread
it then dispatches DoDataItemPrint to the ... same UI thread
it schedules a continuation (via await) so that when that incomplete thing happens, we get back into frmExportSearchResultsProgress_Shown, and since there's probably a sync-context in play here, the sync-context capture (implicit in await) would push us to ... the UI thread
As you can see: everything is happening on the UI thread.
If you want to not block the UI, you need to get off the UI thread. That could be as simple as using Task.Run to invoke DoDataItemPrint, but without knowing what that code contains, it is impossible to know whether you're using thread-bound controls to do the printing. If you are... it will be hard to get away from that.
I found this
Run async method regularly with specified interval
which does half of what I want, but at the same time I want to be able to stop the loop whenever I want and then resume it as well. However while it's stopped, I don't want the infinite loop to keep running where the body gets skipped through a flag.
Basically I don't want this
while (true) {
if (!paused) {
// run work
}
// task delay
}
because then the while loop still runs.
How can I set it so that while its paused, nothing executes?
How can I set it so that while its paused, nothing executes?
That's hard to answer: if you define "pause" as: the object state remains valid while the loop doesn't use any resources then you'll have to stop and restart it (the loop).
All other timers, including Thread.Sleep, Task.Delays etc. will put your thread in idle/suspended mode.
If that's not sufficient for your needs, you'll need to actually stop the "infinite" loop.
It will free up thread related resources as well.
More info about sleep:
Thread.Sleep
More about sleep
You could use System.Threading.Timer and dispose of it while it is not in use and re-create it when you are ready to "resume". These timers are light weight so creating and destroying them on demand is not a problem.
private System.Threading.Timer _timer;
public void StartResumeTimer()
{
if(_timer == null)
_timer = new System.Threading.Timer(async (e) => await DoWorkAsync(e), null, 0, 5000);
}
public void StopPauseTimer()
{
_timer?.Dispose();
_timer = null;
}
public async Task DoWorkAsync(object state)
{
await Task.Delay(500); // do some work here, Task.Delay is just something to make the code compile
}
If you are really adverse to timers and want it to look like a while loop, then you can use TaskCompletionSource<T>:
private TaskCompletionSource<bool> _paused = null;
public async Task DoWork()
{
while (true)
{
if (_paused != null)
{
await _paused.Task;
_paused = null;
}
//run work
await Task.Delay(100);
}
}
public void Pause()
{
_paused = _paused ?? new TaskCompletionSource<bool>();
}
public void UnPause()
{
_paused?.SetResult(true);
}
I'm developing a plugin for a 3D modelling application. For this application, there is also a third party plugin (a render engine) that I would like to automate.
What I do is create a list of Camera List<Camera> cameraViews , iterate trough all of them and tell the render engine to start rendering
foreach ( Camera camera in cameraViews )
{
// tell the modellingApplication to apply camera
modellingApplication.ApplyCameraToView(camera);
// tell the render engine to render the image
string path = "somePathWhereIWantToSaveTheImage"
renderEngine.renderCurrentScene(path)
// .renderCurrentScene() seems to be async, because my code, which is on the UI thread
// continues... so:
// make sure that the image is saved before continuing to the next image
while ( !File.Exists(path) )
{
Thread.Sleep(500);
}
}
However, this wont work. The renderingplugin seems to do some async work but, when doing this async work, it is calling the main thread for retrieving information.
I found a workaround for this: Right after calling the render engine to render, call a MessageBox. This will block the code from continuing but async calls are still beïng handled. I know, this is weird behaviour. Whats even weirder is the fact that my MessageBox gets automatically closed when the renderengine has done calling the UI thread for information and continues in his own process. Making my code continue to the while loop to check if the image is saved on the disk.
foreach ( Camera camera in cameraViews )
{
// tell the modellingApplication to apply camera
modellingApplication.ApplyCameraToView(camera);
// tell the render engine to render the image
string path = "somePathWhereIWantToSaveTheImage"
renderEngine.renderCurrentScene(path)
// .renderCurrentScene() seems to be async, because my code, which is on the UI thread
// continues... so:
// show the messagebox, as this will block the code but not the renderengine.. (?)
MessageBox.Show("Currently processed: " + path);
// hmm, messagebox gets automatically closed, that's great, but weird...
// make sure that the image is saved before continuing to the next image
while ( !File.Exists(path) )
{
Thread.Sleep(500);
}
}
This is wonderful, except for the messagebox part. I don't want to show a messagebox, I just want to pause my code without blocking the entire thread (as calls from the renderengine to the ui thread are still accepted)..
It would've been much easier if the renderengine didn't do his work async..
I don't feel this is the best answer, but it hopefully it's what you are looking for. This is how you block a thread from continuing.
// Your UI thread should already have a Dispatcher object. If you do this elsewhere, then you will need your class to inherit DispatcherObject.
private DispatcherFrame ThisFrame;
public void Main()
{
// Pausing the Thread
Pause();
}
public void Pause()
{
ThisFrame = new DispatcherFrame(true);
Dispatcher.PushFrame(ThisFrame);
}
public void UnPause()
{
if (ThisFrame != null && ThisFrame.Continue)
{
ThisFrame.Continue = false;
ThisFrame = null;
}
}
If you want to still receive and do actions on that thread while blocking intermediately, you can do something like this. This feels, um... kinda hacky, so don't just copy and paste without making sure I didn't make some major mistake typing this out. I haven't had my coffee yet.
// Used while a work item is processing. If you have something that you want to wait on this process. Or you could use event handlers or something.
private DispatcherFrame CompleteFrame;
// Controls blocking of the thread.
private DispatcherFrame TaskFrame;
// Set to true to stop the task manager.
private bool Close;
// A collection of tasks you want to queue up on this specific thread.
private List<jTask> TaskCollection;
public void QueueTask(jTask newTask)
{
//Task Queued.
lock (TaskCollection) { TaskCollection.Add(newTask); }
if (TaskFrame != null) { TaskFrame.Continue = false; }
}
// Call this method when you want to start the task manager and let it wait for a task.
private void FireTaskManager()
{
do
{
if (TaskCollection != null)
{
if (TaskCollection.Count > 0 && TaskCollection[0] != null)
{
ProcessWorkItem(TaskCollection[0]);
lock (TaskCollection) { TaskCollection.RemoveAt(0); }
}
else { WaitForTask(); }
}
}
while (!Close);
}
// Call if you are waiting for something to complete.
private void WaitForTask()
{
if (CompleteFrame != null) { CompleteFrame.Continue = false; }
// Waiting For Task.
TaskFrame = new DispatcherFrame(true);
Dispatcher.PushFrame(TaskFrame);
TaskFrame = null;
}
/// <summary>
/// Pumping block will release when all queued tasks are complete.
/// </summary>
private void WaitForComplete()
{
if (TaskCollection.Count > 0)
{
CompleteFrame = new DispatcherFrame(true);
Dispatcher.PushFrame(CompleteFrame);
CompleteFrame = null;
}
}
private void ProcessWorkItem(jTask taskItem)
{
if (taskItem != null) { object obj = taskItem.Go(); }
}