C# await tasks + infinite loop still freezing the UI - c#

I am trying to get the proper 'structure' for monitoring the state of a game from external source(s) using (Tasks) async/await in order to run the tasks in an infinite loop, however the current way its written seems to just freeze up my UI.
What I have so far:
(in the "state machine" class)
// Start monitoring the game state for changes
public void Start()
{
tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
IsRunning = true;
task = Task.Factory.StartNew(async () =>
{
while (true)
{
await Task.Run(()=>CheckForStateChange());
await Task.Delay(1000); // Pause 1 second before checking state again
}
}, token, TaskCreationOptions.LongRunning, TaskScheduler.FromCurrentSynchronizationContext());
}
Without the above "Task.Delay" line the UI completely freezes up. With the "Task.Delay" line it doesn't freeze up, but if I try to drag the window it skips back to where I began dragging it.
My assumption with the current code is that the 'await Task.Run()' executes and upon completion the 'await Task.Delay()' executes and then on completion returns to the beginning of the while(true) infinite loop. (ie. not running in parallel).
The CheckForStateChange() signature is as follows:
private void CheckForStateChange()
{
// ... A bunch of code to determine and update the current state value of the object
}
Nothing special there, simple non-async method. I have read through lots of examples / questions here on StackOverflow and I used to have CheckForStateChange as returning a Task (with awaitable actions inside the method) and many other iterations of code (with the same results).
Finally I call the Start() method from the main win32 form (button) as follows:
private void btnStartSW_Click(object sender, EventArgs e)
{
// Start the subscription of the event handler
if(!state.IsRunning)
{
state.StateChange += new SummonersWar.StateChangeHandler(OnGameStateChange);
state.Start();
}
}
I think the above code is the simplest form I have written the code structure in so far, but apparently its still not written 'properly'. Any help would be appreciated.
UPDATE:
The publisher side (state machine class):
// ------ Publisher of the event ---
public delegate void StateChangeHandler(string stateText);
public event StateChangeHandler StateChange;
protected void OnStateChange() // TODO pass text?
{
if (StateChange != null)
StateChange(StateText());
}
Where the StateText() method is just a temporary way of retrieving a 'text' representation of the current state (and is really a placeholder at this point until I organize it into a tidier struct)
IsRunning is purely a public bool.
And the handler in the UI thread:
private void OnGameStateChange(string stateText)
{
// Game State Changed (update the status bar)
labelGameState.Text = "State: " + stateText;
}

Why the UI freezes
In terms of the main question: you're already calling your CheckForStateChange via Task.Run, so there is no way that your CheckForStateChange will freeze the UI unless it includes calls which are marshalled back to the UI thread (i.e. Control.Invoke or SynchronizationContext.Post/Send used explicitly, or implicitly via a Task started on the UI TaskScheduler).
The best place to start looking is your StateChange handlers (i.e. StateChangeHandler). Also have a look at where the StateChange event is raised. You'll find thread marshalling code at one of these sites.
Other issues
You're passing the TaskScheduler pointing to the UI SynchronizationContext to the outer task. You're also passing in TaskCreationOptions.LongRunning. In simple terms you're telling the task factory to "start a task on a dedicated thread, and on the current thread". These two are mutually exclusive requirements and you can pretty safely drop them both.
If, as a result of the above, your outer task happens to execute on the UI thread, it won't really trip you up as the inner call is wrapped in Task.Run, but this probably isn't the behaviour you expect.
You are storing the result of Task.Factory.StartNew inside a task field or property. Note, however, that your Task.Factory.StartNew call returns a Task<Task>, so the saved Task instance will transition to completed state almost immediately unless you call Unwrap on it and get to the inner task. To avoid this entire mess, just use Task.Run to create the outer task (as it has Unwrap semantics built in). If you do that, you can ditch the inner Task.Run completely, like so:
public bool IsRunning
{
get
{
return task.Status == TaskStatus.Running;
}
}
public void Start()
{
tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
task = Task.Run(async () =>
{
while (true)
{
CheckForStateChange(token);
token.ThrowIfCancellationRequested();
await Task.Delay(1000); // Pause 1 second before checking state again
}
}, token);
// Uncomment this and step through `CheckForStateChange`.
// When the execution hangs, you'll know what's causing the
// postbacks to the UI thread and *may* be able to take it out.
// task.Wait();
}
Since you have a CancellationToken you need to be passing it to CheckForStateChange, and checking it periodically - otherwise it only gets checked once, when the Task is started, and then never again.
Note that I have also provided a different IsRunning implementation. Volatile state is hard to get right. If the framework is giving it to you for free, you should use it.
Final word
Overall this entire solution feels like a bit of a crutch for something that should be done more reactively - but I can think of scenarios where this sort of design is valid. I'm just not convinced that yours is really one of them.
EDIT: how to find what's blocking the UI
I'll get downvoted to oblivion for this, but here goes:
The sure way to find what's causing postbacks to the UI thread is to deadlock with it. There's plenty of threads here on SO telling you how to avoid that, but in your case - we'll cause it on purpose and you'll know exactly what calls you need to avoid when you're polling for changes - although whether or not it will be possible to avoid these calls, remains to be seen.
I've put a task.Wait instruction at the end of my code snippet. Provided that you call Start on the UI thread, that should cause a deadlock with something inside your CheckForStateChange, and you will know what it is that you need to work around.

Related

Questions about what runs on the main thread during c#'s async await

I'm having trouble understanding what runs on the main thread during an async await operation and would be grateful for some answers.
Let's say I have a button that is supposed to log the user in.
it is supposed to block all other user input while the login process transpires, show a progress view and then when the result comes in display it
and here is the method that performs the log in
button_clicked(object sender, EventArgs e) {
do_login(); //I do not await the result
do_some_other_stuff(); //this doesn't actually exist I just put it here to ask my questions
}
async Task do_login() {
string user_name = txtUser.Text;
string password = txtPassword.Text;
show_progress(true); //this is not an async method;
string error = await _login.do_login(user_name, password);//this is an async method that can take up to 20 seconds to complete;
show_progress(false);
if (error != null) {
show_error(error);
} else {
show_next_screen();
}
}
I have two questions on the above example
a) What will be run on the main thread?
If I understand it correctly only _login.do_login will be run on a seperate thread, all others will be on the main thread, is this correct?
b) In what order will the methods be executed?
Again if I understand it correctly, it will be :
do_login()
show_progress(true);
_login.do_login starts;
do_some_other_stuff();
_login.do_login finishes;
show_progress(false);
and it will continue from there
is this correct? if not, how can I achieve such a behaviour?
c) If my code above is correct then why do I keep receiving a warning that do_login() is not awaited? I do not wish to await it I just want it to run what it can and return when it wants, should I ignore that warning?
Technically, depending on the implementation of do_login, everything could run in the main thread. In this case I assume you're contacting a web server, so that part won't, but this is not always true. And asynchronous operation does not necessarily executes in another thread. One operation is asynchronous when:
It doesn't block the calling thread.
Usually, UI threads run an 'event loop'. So an asynchronous task could simply put a new piece of work into the event queue to be executed whenever the scheduler determines, but in the same thread. In this case you don't use two threads, but still, you don't have to wait for the task to complete and you don't know when it'll finish.
To be precise, all the code in your post will run in the main thread. Only the part in do_login that manages the connection with the server, waiting and retrieving data will execute asynchronously.
You're mostly right about the sequence, with a few adjustments:
do_login() (until the await)
login._do_login() starts executing
do_some_other_stuff()
...
login.do_login finishes
show_progress()
The answer to your main question is: it depends. The _login.do_login method will likely be put onto its own thread, but it actually depends on the .NET task scheduler. In WPF and ASP.NET it will be scheduled onto the thread pool if it doesn't immediately return a completed task.
The important part is that you know it will not block execution of the calling (in your case, the main) thread. Your understanding of the method flow is correct since you don't await do_login.
As far as the warning goes; you can mark do_login as async void to avoid it, though generally you only do that for event handlers which can then await a Task returning method. If you do go the async void route; make sure to put a try/catch in as such methods will throw all the way up to the root handler and can cause your app to crash.

Could a BeginInvokeOnMainThread method be looping and causing a memory leak?

I have an application that works but after a while when I debug on my iPhone it hangs the phone and the only way I can recover is a hard reset of the button on the side and the home button.
First of all, could that be because my application has a memory leak?
Here's the code for the application. In particular, I am looking at the BeginInvokeOnMainThread method. Can someone tell me if they can see if there could be any problems with the way that it is implemented? Also, what's the purpose of the .ContinueWith((arg).
namespace Japanese
{
public partial class PhrasesFrame : Frame
{
CancellationTokenSource cts = new CancellationTokenSource();
public PhrasesFrame(PhrasesPage phrasesPage)
{
InitializeComponent();
this.phrasesPage = phrasesPage;
AS.phrasesFrame = this;
Device.BeginInvokeOnMainThread(() => ShowCards(cts.Token).ContinueWith((arg) => { }));
}
public void Disappearing()
{
cts.Cancel();
}
public async Task ShowCards(CancellationToken ct)
{
AS.cardCountForSelectedCategories = App.DB.GetCardCountForSelectedCategories();
while (!ct.IsCancellationRequested)
{
await Task.Delay(500);
}
}
}
}
ContinueWith
First, let's address your question about .ContinueWith((arg) => { })). ContinueWith tells more code to execute once the original Task has completed. In our case, the code inside of ContinueWith will run once Device.BeginInvokeOnMainThread(() => ShowCards(cts.Token) has finished.
In this case, there is no code inside of ContinueWith, so we can remove it.
Freezing
Yes, I can see that this code has potential to freeze the UI.
BeginInvokeOnMainThread will queue an Action to run on the Main Thread (also known as the UI Thread). The Main Thread is constantly listening for user input (tapping a button on the screen, pinch-to-zoom, etc.), and if this thread is busy doing a long-running task, it will not be able to respond to a user's input until it has finished; thus your app will appear frozen.
The code await Task.Delay(500); is being called by the Main Thread. We are thus telling the Main Thread to freeze itself for 500 milliseconds, and looping that indefinitely.
One solution would be to wrap this code in Task.Run which would put it in a background-thread and free the Main Thread to listen/respond to user input.
Task.Run(async () =>
{
while (!ct.IsCancellationRequested)
{
await Task.Delay(500);
}
}
More Threading Recommendations
Only use BeginInvokeOnMainThread when you need to update the UI. 99% of code can run on a background thread with no problems. The 1%, however, is code that updates the UI; any code that updates the UI must be run on the Main Thread.
If a task that takes longer than the refresh rate of the screen to execute, perform it on a background thread. For example, if the screen's refresh rate is 60Hz, it is updating 60-times per second, every 16.7ms. So if we have a block of code that takes 20ms to execute, we need to execute it on a background thread to ensure that we don't freeze the app and drop any frames.
The code above looks like it is accessing a database, which I would highly recommend moving to a background thread like so
await Task.Run(() => AS.cardCountForSelectedCategories = App.DB.GetCardCountForSelectedCategories());
First, if you are concerned about a memory leak, you can check for low-memory warnings in the device logs (accessible through XCode), or override the ReceiveMemoryWarning method in your app delegate to log an error.
Secondly, there's nothing obviously wrong with the way you're calling BeginInvokeOnMainThread that would cause a leak. The ContinueWith is a no-op that doesn't affect the operation of the code - I'm guessing it's there to avoid a compiler warning that you're not awaiting the task.
Thirdly, if you suspect that this code is causing a leak, you should use logging and/or breakpoints to confirm that it's behaving as expected. Is the task correctly cancelled when you navigate away from the page? Do you see multiple instances of of the ShowCards task running? If this code turns out to be behaving correctly, then the source of the hang lies elsewhere in your app. For instance, it looks like you're making a database call twice a second - maybe it's not cleaning up resources properly.

Why is this code running synchronously?

I am trying to understand concurrency by doing it in code. I have a code snippet which I thought was running asynchronously. But when I put the debug writeline statements in, I found that it is running synchronously. Can someone explain what I need to do differently to push ComputeBB() onto another thread using Task.Something?
Clarification I want this code to run ComputeBB in some other thread so that the main thread will keep on running without blocking.
Here is the code:
{
// part of the calling method
Debug.WriteLine("About to call ComputeBB");
returnDTM.myBoundingBox = await Task.Run(() => returnDTM.ComputeBB());
Debug.WriteLine("Just called await ComputBB.");
return returnDTM;
}
private ptsBoundingBox2d ComputeBB()
{
Debug.WriteLine("Starting ComputeBB.");
Stopwatch sw = new Stopwatch(); sw.Start();
var point1 = this.allPoints.FirstOrDefault().Value;
var returnBB = new ptsBoundingBox2d(
point1.x, point1.y, point1.z, point1.x, point1.y, point1.z);
Parallel.ForEach(this.allPoints,
p => returnBB.expandByPoint(p.Value.x, p.Value.y, p.Value.z)
);
sw.Stop();
Debug.WriteLine(String.Format("Compute BB took {0}", sw.Elapsed));
return returnBB;
}
Here is the output in the immediate window:
About to call ComputeBB
Starting ComputeBB.
Compute BB took 00:00:00.1790574
Just called await ComputBB.
Clarification If it were really running asynchronously it would be in this order:
About to call ComputeBB
Just called await ComputBB.
Starting ComputeBB.
Compute BB took 00:00:00.1790574
But it is not.
Elaboration
The calling code has signature like so: private static async Task loadAsBinaryAsync(string fileName) At the next level up, though, I attempt to stop using async. So here is the call stack from top to bottom:
static void Main(string[] args)
{
aTinFile = ptsDTM.CreateFromExistingFile("TestSave.ptsTin");
// more stuff
}
public static ptsDTM CreateFromExistingFile(string fileName)
{
ptsDTM returnTin = new ptsDTM();
Task<ptsDTM> tsk = Task.Run(() => loadAsBinaryAsync(fileName));
returnTin = tsk.Result; // I suspect the problem is here.
return retunTin;
}
private static async Task<ptsDTM> loadAsBinaryAsync(string fileName)
{
// do a lot of processing
Debug.WriteLine("About to call ComputeBB");
returnDTM.myBoundingBox = await Task.Run(() => returnDTM.ComputeBB());
Debug.WriteLine("Just called await ComputBB.");
return returnDTM;
}
I have a code snippet which I thought was running asynchronously. But when I put the debug writeline statements in, I found that it is running synchronously.
await is used to asynchronously wait an operations completion. While doing so, it yields control back to the calling method until it's completion.
what I need to do differently to push ComputeBB() onto another thread
It is already ran on a thread pool thread. If you don't want to asynchronously wait on it in a "fire and forget" fashion, don't await the expression. Note this will have an effect on exception handling. Any exception which occurs inside the provided delegate would be captured inside the given Task, if you don't await, there is a chance they will go about unhandled.
Edit:
Lets look at this piece of code:
public static ptsDTM CreateFromExistingFile(string fileName)
{
ptsDTM returnTin = new ptsDTM();
Task<ptsDTM> tsk = Task.Run(() => loadAsBinaryAsync(fileName));
returnTin = tsk.Result; // I suspect the problem is here.
return retunTin;
}
What you're currently doing is synchronously blocking when you use tsk.Result. Also, for some reason you're calling Task.Run twice, once in each method. That is unnecessary. If you want to return your ptsDTM instance from CreateFromExistingFile, you will have to await it, there is no getting around that. "Fire and Forget" execution doesn't care about the result, at all. It simply wants to start whichever operation it needs, if it fails or succeeds is usually a non-concern. That is clearly not the case here.
You'll need to do something like this:
private PtsDtm LoadAsBinary(string fileName)
{
Debug.WriteLine("About to call ComputeBB");
returnDTM.myBoundingBox = returnDTM.ComputeBB();
Debug.WriteLine("Just called ComputeBB.");
return returnDTM;
}
And then somewhere up higher up the call stack, you don't actually need CreateFromExistingFiles, simply call:
Task.Run(() => LoadAsBinary(fileName));
When needed.
Also, please, read the C# naming conventions, which you're currently not following.
await's whole purpose is in adding the synchronicity back in asynchronous code. This allows you to easily partition the parts that are happenning synchronously and asynchronously. Your example is absurd in that it never takes any advantage whatsoever of this - if you just called the method directly instead of wrapping it in Task.Run and awaiting that, you would have had the exact same result (with less overhead).
Consider this, though:
await
Task.WhenAll
(
loadAsBinaryAsync(fileName1),
loadAsBinaryAsync(fileName2),
loadAsBinaryAsync(fileName3)
);
Again, you have the synchronicity back (await functions as the synchronization barrier), but you've actually performed three independent operations asynchronously with respect to each other.
Now, there's no reason to do something like this in your code, since you're using Parallel.ForEach at the bottom level - you're already using the CPU to the max (with unnecessary overhead, but let's ignore that for now).
So the basic usage of await is actually to handle asynchronous I/O rather than CPU work - apart from simplifying code that relies on some parts of CPU work being synchronised and some not (e.g. you have four threads of execution that simultaneously process different parts of the problem, but at some point have to be reunited to make sense of the individual parts - look at the Barrier class, for example). This includes stuff like "making sure the UI doesn't block while some CPU intensive operation happens in the background" - this makes the CPU work asynchronous with respect to the UI. But at some point, you still want to reintroduce the synchronicity, to make sure you can display the results of the work on the UI.
Consider this winforms code snippet:
async void btnDoStuff_Click(object sender, EventArgs e)
{
lblProgress.Text = "Calculating...";
var result = await DoTheUltraHardStuff();
lblProgress.Text = "Done! The result is " + result;
}
(note that the method is async void, not async Task nor async Task<T>)
What happens is that (on the GUI thread) the label is first assigned the text Calculating..., then the asynchronous DoTheUltraHardStuff method is scheduled, and then, the method returns. Immediately. This allows the GUI thread to do whatever it needs to do. However - as soon as the asynchronous task is complete and the GUI is free to handle the callback, the execution of btnDoStuff_Click will continue with the result already given (or an exception thrown, of course), back on the GUI thread, allowing you to set the label to the new text including the result of the asynchronous operation.
Asynchronicity is not an absolute property - stuff is asynchronous to some other stuff, and synchronous to some other stuff. It only makes sense with respect to some other stuff.
Hopefully, now you can go back to your original code and understand the part you've misunderstood before. The solutions are multiple, of course, but they depend a lot on how and why you're trying to do what you're trying to do. I suspect you don't actually need to use Task.Run or await at all - the Parallel.ForEach already tries to distribute the CPU work over multiple CPU cores, and the only thing you could do is to make sure other code doesn't have to wait for that work to finish - which would make a lot of sense in a GUI application, but I don't see how it would be useful in a console application with the singular purpose of calculating that single thing.
So yes, you can actually use await for fire-and-forget code - but only as part of code that doesn't prevent the code you want to continue from executing. For example, you could have code like this:
Task<string> result = SomeHardWorkAsync();
Debug.WriteLine("After calling SomeHardWorkAsync");
DoSomeOtherWorkInTheMeantime();
Debug.WriteLine("Done other work.");
Debug.WriteLine("Got result: " + (await result));
This allows SomeHardWorkAsync to execute asynchronously with respect to DoSomeOtherWorkInTheMeantime but not with respect to await result. And of course, you can use awaits in SomeHardWorkAsync without trashing the asynchronicity between SomeHardWorkAsync and DoSomeOtherWorkInTheMeantime.
The GUI example I've shown way above just takes advantage of handling the continuation as something that happens after the task completes, while ignoring the Task created in the async method (there really isn't much of a difference between using async void and async Task when you ignore the result). So for example, to fire-and-forget your method, you could use code like this:
async void Fire(string filename)
{
var result = await ProcessFileAsync(filename);
DoStuffWithResult(result);
}
Fire("MyFile");
This will cause DoStuffWithResult to execute as soon as result is ready, while the method Fire itself will return immediately after executing ProcessFileAsync (up to the first await or any explicit return someTask).
This pattern is usually frowned upon - there really isn't any reason to return void out of an async method (apart from event handlers); you could just as easily return Task (or even Task<T> depending on the scenario), and let the caller decide whether he wants his code to execute synchronously in respect to yours or not.
Again,
async Task FireAsync(string filename)
{
var result = await ProcessFileAsync(filename);
DoStuffWithResult(result);
}
Fire("MyFile");
does the same thing as using async void, except that the caller can decide what to do with the asynchronous task. Perhaps he wants to launch two of those in parallel and continue after all are done? He can just await Task.WhenAll(Fire("1"), Fire("2")). Or he just wants that stuff to happen completely asynchronously with respect to his code, so he'll just call Fire("1") and ignore the resulting Task (of course, ideally, you at the very least want to handle possible exceptions).

Calling method from DoWork handler?

I am attempting to use a BackgroundWorker to complete a task. I have got the worker to run correctly, under the DoWork method it then calls another method which executes but then I face my problem: when that method tries to call another method it does not succeed and does not throw an exception and I can only see this as being something I am doing wrong with BackgroundWorker since when running on the UI thread for testing the methods execute as intended.
Here is where I run my worker:
private void btnAddShots_Click(object sender, EventArgs e)
{
backgroundWorker.RunWorkerAsync();
}
Here is my DoWork method:
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
int noOfShots = dataGridShots.Rows.Count - 1;
int count = 0;
while (count < noOfShots)
{
addTaskPair(dataGridShots.Rows[count].Cells[0].Value.ToString(),
dataGridShots.Rows[count].Cells[1].Value.ToString(),
dataGridShots.Rows[count].Cells[2].Value.ToString());
count += 1;
}
}
Here is a stripped down version of addTaskPair method that is called by my worker:
private void addTaskPair(string taskName, string taskDescription, string taskPriority)
{
try
{
Task trackingTask = new Task();
trackingTask.content = taskName;
trackingTask.description = taskDescription;
trackingTask.priority = taskPriority;
string trackingJson = JsonConvert.SerializeObject(trackingTask);
trackingJson = "{ \"todo-item\":" + trackingJson + " }";
string jsonResponse;
jsonResponse = postJSON(trackingJson, teamworkURL + "/tasklists/"
+ todoLists.todoLists[cmbTrackingList.SelectedIndex].id + "/tasks.json");
}
catch (Exception e)
{
debugMessage(e.ToString());
}
}
Within the above sample you will see that I call the method postJSON, this is where I hit my wall. Through testing I have verified that the above method runs, but the postJSON method does not run at all when being called from within this thread.
I have seen many references to Invoking when researching this problem but they all seem to apply to changing ui controls which I do not need to do (though a progress bar is manipulated using the ProgressChanged BackgroundWorker event).
I can clarify my problem more if needs be but I am really hoping for assistance with this since I have never successfully worked with backgroundworker or threading before (I am not a professional as I'm sure you can tell from my code).
You're working with the UI controls from the BackgroundWorker.DoWork event handler. Don't do that.
Collect the data before starting the BackgroundWorker, and pass it as an argument to the RunWorkerAsync method. Do not touch the UI from the BackgroundWorker.DoWork - progress updates through the ReportProgress method are fine.
Also, if you're running on .NET 4.5+, you might want to consider using the new Task pattern instead. It still requires you to collect the data to process beforehand, but it's a lot easier to work with:
(EDIT: As Peter suggested, the invalid access is happening in cmbTrackingList.SelectedIndex; I've included it in the code below. That's exactly the reason I suggested using static methods for the operations happening in a separate thread - it makes you think a lot more about the data you're working with)
var todoList = todoLists.todoLists[cmbTrackingList.SelectedIndex];
var data =
dataGridShots
.Rows
.Select
(
i =>
new
{
TaskName = i.Cells[0].Value.ToString(),
TaskDescription = i.Cells[1].Value.ToString(),
TaskPriority = i.Cells[2].Value.ToString()
}
)
.ToArray();
var result =
await Task.Run
(
() =>
foreach (var row in data)
handleRowData(row.TaskName, row.TaskDescription, row.TaskPriority, todoList)
);
Now that you're this far, you might notice that it shouldn't be too hard to make your postJson method asynchronous as well (there's plenty of ways to make HTTP requests asynchronously) - this will allow you to make your whole code asynchronous without blocking any thread.
Multi-threading is hard. Always try to work with the highest possible abstraction, and avoid any shared state. If you do need shared state, you need to synchronize every single access to it, from each of the threads - try to avoid that (a good practice being having the methods executing on different threads static, so that you don't accidentaly touch shared state).
From your description, it seems that the access of the DataGrid components is not the problem. That is, it seems those statements get executed correctly, and the addTaskPair() method is successfully called, but the postJSON() method is not.
Given that, I suspect that the evaluation of cmbTrackingList.SelectedIndex is what's throwing an exception and interrupting the thread.
That said, the advice is still the same: keep the UI-related stuff on the UI thread, and only run the other stuff outside the UI thread. Given the code you've posted, it seems likely that the only thing that really ought to be asynchronous (i.e. run in the background, so as to not delay the UI thread too much) is the call to postJSON(). Presumably this is a synchronous network call, and so could take awhile. The other stuff should run deterministically and quickly.
Given that, here's how I'd refactor the code, taking advantage of the new async/await feature:
private async void btnAddShots_Click(object sender, EventArgs e)
{
int noOfShots = dataGridShots.Rows.Count - 1;
int count = 0;
while (count < noOfShots)
{
await addTaskPair(dataGridShots.Rows[count].Cells[0].Value.ToString(),
dataGridShots.Rows[count].Cells[1].Value.ToString(),
dataGridShots.Rows[count].Cells[2].Value.ToString());
count += 1;
}
}
private async Task addTaskPair(string taskName, string taskDescription, string taskPriority)
{
try
{
TaskData trackingTask = new TaskData();
trackingTask.content = taskName;
trackingTask.description = taskDescription;
trackingTask.priority = taskPriority;
string trackingJson = JsonConvert.SerializeObject(trackingTask);
trackingJson = "{ \"todo-item\":" + trackingJson + " }";
string jsonResponse;
string url = teamworkURL + "/tasklists/"
+ todoLists.todoLists[cmbTrackingList.SelectedIndex].id + "/tasks.json";
jsonResponse = await Task.Run(() => postJSON(trackingJson, url));
}
catch (Exception e)
{
debugMessage(e.ToString());
}
}
NOTE: in the above I changed the name of your own Task type to TaskData. I strongly recommend you pick a name other than Task, due to the pervasive use of the .NET Task type throughout the modern .NET API.
In the above, most of the code will run on the UI thread. The async methods are rewritten by the compiler to return at any await statement, and to resume execution of that method when the awaited Task has completed. Note that the async method only returns when there is finally a Task object to await; so in the above, the btnAddShots_Click() method will initially return once the addTaskPair() method has called Task.Run() and itself has returned at the await statement.
Important: in this context, calling and awaiting an asynchronous method from the UI thread causes the framework to run the rest of the method back on the UI thread. That is, when the asynchronous operation has completed, control of the execution of the code is returned back to the UI thread where you started.
It's this feature that makes all of this work correctly, so it's useful to make sure you understand it. :)
The call to postJSON() is executed in a separate thread, using the Task object that is created by the Task.Run() method. Since that will be executing on other than the UI thread, I've moved the computation of its URL argument to a local variable just before the call to Task.Run(), which variable is then passed to the postJSON() method when it's called in the task thread. Doing this ensures that the evaluation of cmbTrackingList.SelectedIndex is done in the UI thread.
EDIT:
Noting that the OP has commented he is using .NET 4 and not 4.5 (in which the async/await feature was officially released), I offer this slightly more awkward alternative, which still preserves the execution characteristics of the preferable 4.5-compatible version above. While one can install the async/await features on VS2010 (and IMHO that's a better way to go), this alternative allows for "pure" .NET 4 code while still achieving basically the same runtime result.
private void btnAddShots_Click(object sender, EventArgs e)
{
Action<Task> continuation = null;
TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
int noOfShots = dataGridShots.Rows.Count - 1;
int count = 0;
// Note that the continuation delegate is chained, attaching itself as
// the continuation for each successive task, thus achieving a looping
// mechanism.
continuation = task =>
{
if (count < noOfShots)
{
addTaskPair(dataGridShots.Rows[count].Cells[0].Value.ToString(),
dataGridShots.Rows[count].Cells[1].Value.ToString(),
dataGridShots.Rows[count].Cells[2].Value.ToString())
.ContinueWith(continuation, uiScheduler);
count += 1;
}
}
// Invoking the continuation delegate directly gets the ball rolling
continuation(null);
}
private Task addTaskPair(string taskName, string taskDescription, string taskPriority)
{
try
{
TaskData trackingTask = new TaskData();
trackingTask.content = taskName;
trackingTask.description = taskDescription;
trackingTask.priority = taskPriority;
string trackingJson = JsonConvert.SerializeObject(trackingTask);
trackingJson = "{ \"todo-item\":" + trackingJson + " }";
string url = teamworkURL + "/tasklists/"
+ todoLists.todoLists[cmbTrackingList.SelectedIndex].id + "/tasks.json";
// NOTE: must explicitly specify TaskScheduler.Default, because
// the default scheduler in the context of a Task is whatever the
// current scheduler is, which while executing a continuation would
// be the UI scheduler, not TaskScheduler.Default.
return Task.Factory.StartNew(() => postJSON(trackingJson, url),
CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default)
.ContinueWith(task =>
{
if (task.Exception != null)
{
// Task exceptions are wrapped in an AggregateException
debugMessage(task.Exception.InnerException.ToString());
}
else
{
string jsonResponse = task.Result;
// do something with jsonResponse?
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
catch (Exception e)
{
debugMessage(e.ToString());
}
}
Notes:
The task scheduler for the current synchronization context is used for each continuation. This ensures that the continuation itself is executed back on the UI thread, where you can safely interact with UI objects.
The most awkward part of this IMHO is that your while loop becomes an if statement, since the execution of the loop spans multiple method invocations.
While async/await would normally allow normal exception-handling syntax, when using explicit continuations you don't have this option. But Task will wrap any exceptions that occur in an AggregateException instance, which you can use to get at the real exception and report it.
TaskFactory.StartNew() has a somewhat subtle behavior: the scheduler it uses to run the given task is TaskScheduler.Current. The first time the addTaskPair() method is called, there is no current task, and so TaskScheduler.Current returns the default (i.e. thread pool) scheduler. But every subsequent time the addTaskPair() method is called, it is from within a task continuation, and so TaskScheduler.Current would return the scheduler being used to execute the continuation. Of course, we've intentionally made this the UI scheduler and running the new postJSON() task using that scheduler would defeat the purpose, as it would just execute synchronously on the current thread. So it's imperative here to specify the scheduler we want, i.e. TaskScheduler.Default, corresponding to the thread pool scheduler.
Without async/await, it's a bit harder to get things just right. It's syntactically more verbose, but IMHO it's still a reasonably good option, as it substantially preserves the desired, imperative structure of the code. In particular, you get to keep the flow of execution in the UI thread, making access of UI objects trivial, and forking off the long-running operation(s) only as needed.
(I should also point out that this .NET 4 version is not strictly exactly what the compiler would generate for you when using async/await. It's very similar, but not quite the same. Also, I will point out that while implementing a method with a single await in this way is not too bad, it gets a bit out of hand if you want multiple continuations in the same method. It's possible, but at that point I'd think the urge to just upgrade to the latest version of C# would be very compelling :) ).
Finally…
If after all of the above, you want to stick with BackgroundWorker, it should be possible to avoid the exception that's occurring with a relatively simple change to your code:
int selectedIndex = (int)Invoke((Func<int>)(() => cmbTrackingList.SelectedIndex));
jsonResponse = postJSON(trackingJson, teamworkURL + "/tasklists/"
+ todoLists.todoLists[selectedIndex].id + "/tasks.json");
I.e. just use the Control.Invoke() method to invoke on the UI thread an anonymous method that will return the value of cmbTrackingList.SelectedIndex. The Control.Invoke() method will receive the returned value, and will in turn return it to you. Since Control.Invoke() is general-purpose, its object return type must be cast to the type you know is being returned.
This ensures that the cmbTrackingList object is accessed only on the UI thread. I'll also note that if this index is not expected to change while the background processing is going on (or is specifically not supposed to), then yet another alternative would be to retrieve the value in your btnAddShots_Click() method and then pass it down to the DoWork event handler, which in turn would pass it to the addTaskPair() method where it's needed.
I put this option last because I really believe that learning the async/await feature is important and worthwhile, and that while the BackgroundWorker class has served us well over the years, it's essentially been deprecated by the new features. But I also readily admit that BackgroundWorker is still a fine way to do things, and can be made to work in your scenario.

Unexpected behaviour after returning from await

I know there are a lot of questions about async/await, but I couldn't find any answer to this.
I've encountered something I don't understand, consider the following code:
void Main()
{
Poetry();
while (true)
{
Console.WriteLine("Outside, within Main.");
Thread.Sleep(200);
}
}
async void Poetry()
{
//.. stuff happens before await
await Task.Delay(10);
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Inside, after await.");
Thread.Sleep(200);
}
}
Obviously, on the await operator, the control returns to the caller, while the method being awaited, is running on the background. (assume an IO operation)
But after the control comes back to the await operator, the execution becomes parallel, instead of (my expectation) remaining single-threaded.
I'd expect that after "Delay" has been finished the thread will be forced back into the Poetry method, continues from where it left.
Which it does. The weird thing for me, is why the "Main" method keeps running? is that one thread jumping from one to the other? or are there two parallel threads?
Isn't it a thread-safety problem, once again?
I find this confusing. I'm not an expert. Thanks.
I have a description on my blog of how async methods resume after an await. In essence, await captures the current SynchronizationContext unless it is null in which case it captures the current TaskScheduler. That "context" is then used to schedule the remainder of the method.
Since you're executing a Console app, there is no SynchronizationContext, and the default TaskScheduler is captured to execute the remainder of the async method. That context queues the async method to the thread pool. It is not possible to return to the main thread of a Console app unless you actually give it a main loop with a SynchronizationContext (or TaskScheduler) that queues to that main loop.
Read It's All About the SynchronizationContext and I'm sure it'll become less confusing. The behavior you're seeing makes perfect sense. Task.Delay uses Win32 Kernel Timer APIs internally (namely, CreateTimerQueueTimer). The timer callback is invoked on a pool thread, different from your Main thread. That's where the rest of Poetry after await continues executing. This is how the default task scheduler works, in the absence of synchronization context on the original thread which initiated the await.
Because you don't do await the Poetry() task (and you can't unless you return Task instead of void), its for loop continues executing in parallel with the while loop in your Main. Why, and more importantly, how would you expect it to be "forced" back onto the Main thread? There has to be some explicit point of synchronization for this to happen, the thread cannot simply get interrupted in the middle of the while loop.
In a UI application, the core message loop may serve as such kind of synchronization point. E.g. for a WinForms app, WindowsFormsSynchronizationContext would make this happen. If await Task.Delay() is called on the main UI thread, the code after await would asynchronously continue on the main UI thread, upon some future iteration of the message loop run by Application.Run.
So, if it was a UI thread, the rest of the Poetry wouldn't get executed in parallel with the while loop following the Poetry() call. Rather, it would be executed when the control flow had returned to the message loop. Or, you might explicitly pump messages with Application.DoEvents() for the continuation to happen, although I wouldn't recommend doing that.
On a side note, don't use async void, rather use async Task, more info.
When you call an async routine the purpose of this is to allow the program to run a method while still allowing the calling routine, form or application to continue to respond to user input (in other words, continue execution normally). The "await" keyword pauses execution at the point it is used, runs the task using another thread then returns to that line when the thread completes.
So, in your case if you want the main routine to pause until the "Poetry" routine is done you need to use the await keyword something like this:
void async Main()
{
await Poetry();
while (true)
{
Console.WriteLine("Outside, within Main.");
Thread.Sleep(200);
}
}
You will also need to change the definition for Poetry to allow the await keyword to be used:
async Task Poetry()
Because this question really intrigued me I went ahead and wrote an example program you can actually compile. Just create a new console application and paste this example in. You can see the result of using "await" versus not using it.
class Program
{
static void Main(string[] args)
{
RunMain();
// pause long enough for all async routines to complete (10 minutes)
System.Threading.Thread.Sleep(10 * 60 * 1000);
}
private static async void RunMain()
{
// with await this will pause for poetry
await Poetry();
// without await this just runs
// Poetry();
for (int main = 0; main < 25; main++)
{
System.Threading.Thread.Sleep(10);
Console.WriteLine("MAIN [" + main + "]");
}
}
private static async Task Poetry()
{
await Task.Delay(10);
for (int i = 0; i < 10; i++)
{
Console.WriteLine("IN THE POETRY ROUTINE [" + i + "]");
System.Threading.Thread.Sleep(10);
}
}
}
Happy testing! Oh, and you can still read more information here.
I'd like to answer my own question here.
Some of you gave me great answers which all helped me understand better (and were thumbed up). Possibly no one gave me a full answer because I've failed to ask the full question. In any case someone will encounter my exact misunderstanding, I'd like this to be the first answer (but I'll recommend to look at some more answers below).
So, Task.Delay uses a Timer which uses the operating system to fire an event after N milliseconds. after this period a new pooled thread is created, which basically does almost nothing.
The await keyword means that after the thread has finished (and it's doing almost nothing) it should continue to whatever comes after the await keyword.
Here comes the synchronization context, as mentioned in other answers.
If there is no such context, the same newly-created-pooled-thread will continue running what ever comes after the await.
If there is a synchronizing context, the newly-created-pool-thread, will only push whatever comes after the await, into synchronizing context.
For the sake of it, here are a few points I didn't realize:
The async/await are not doing anything which wasn't (technologly speaking) possible before. Just maybe amazingly clumsy.
It's is just a language support for some of .NET 4.5 classes.
It's much like yield return. It may break your method into a few methods, and may even generate a class behind, and use some methods from the BCL, but nothing more.
Anyway, I recommend reading C# 5.0 In A Nutshell's chapter "Concurrency and Asynchrony". It helped me a lot. It is great, and actually explains the whole story.

Categories