Not using await Task.Delay(x) (x>0) makes UI thread freeze - c#

I have this function that bruteforces GUIDs to calculate IDs (GUIDs are hash calculations of the ID, so reversing the procedure can't be done by nothing else but bruteforcing through 2^32-1 (yes, that's right) possible IDs.
I used await Task.Delay(1); to "refresh" the UI thread so everything looks natural, however this slows down the process too much, but if I were to use await Task.Delay(0); the whole app would freeze and i wouldn't know the current progress of the search (shown as the 2 IProgresss for the progress bar.
async public Task<Dictionary<string, string>> GetSteamIds(Dictionary<string,string> GUIDs, IProgress<UInt32> progress, IProgress<double> progress2)
{
List<string> Progress = new List<string>();
foreach(string GUID in GUIDs.Keys)
{
Progress.Add(GUID);
}
for (;Iteration <= UInt32.MaxValue;Iteration++)
{
await Task.Delay(0);
string guid = CalculateGUID(MinAcc+Iteration);
if(GUIDs.ContainsKey(guid))
{
GUIDs[guid] = Convert.ToString((Int64)(MinAcc + Iteration));
}
progress.Report(Iteration);
progress2.Report((1.0 * Iteration / UInt32.MaxValue * 100));
if(Progress.Count==0)
{
if (progress2 != null)
{
progress2.Report(100);
}
break;
}
}
return GUIDs;
}
I do not know how to mitigate this and even though my whole method is async, and the call is async, it still sits on the "UI thread" and I don't know how to work around this.
Naturally, I would appreciate a good answer that works, but I would also appreciate some elaboration to how this all works. I've read quite a bit of articles but none really explained it to me in a way for me to understand everything.

Adding the async keyword to a method doesn't actually make the method asynchronous, it just means that you're allowed to use the await keyword inside of the method. You've written an entirely synchronous CPU bound method and just tagged the method as async, making it still just a synchronous CPU bound method, and calling a synchronous CPU bound method from the UI thread is going to block the UI thread, preventing other UI actions from taking place.
Adding in an await Task.Delay(1) is making your method still do all of its long running CPU bound work in the UI thread, it's just stopping every once in a while to move to the end of the line, allowing other operations to run. Not only is this slowing down your work, it also means that the UI thread isn't able to perform other UI operations the vast majority of the time as this process is using up so much of its time.
The solution is to simply not do the long running CPU bound work in the UI thread at all, and to offload that work to another thread.
Since this method of your isn't actually asynchronous, you should remove the async keyword from the method (and change the return type accordingly). When you call this method, if you happen to need to do so from the UI thread, simply wrap it in a call to Task.Run which will offload it to a thread pool thread. If that UI operation need to do work after it has completed with the result, then that UI operation can await the result of Task.Run, and that method should be marked as async, because it really is doing its work asynchronously.

Your GetSteamIds method is entirely synchronous and CPU-bound, so it should have a synchronous signature:
public Dictionary<string, string> GetSteamIds(Dictionary<string,string> GUIDs, IProgress<UInt32> progress, IProgress<double> progress2);
Now, when you want to invoke this (synchronous, CPU-bound) method from the UI thread, use await Task.Run to run it on a threadpool thread and (asynchronously) get the result of the method:
var results = await Task.Run(() => GetSteamIds(guids, progress, progress2));
If you're using Progress<T>, then it will take care of handling progress updates on the UI thread for you; there's no need for the awkward Dispatcher or Control.Invoke.
On a side note, with a very tight CPU-bound loop like this, you may find your progress reports themselves will slow down your UI and degrade your user experience. In that case, consider using my ObservableProgress, an IProgress<T> implementation that throttles progress updates.

Don't run the task on the UI thread.
You could call it like this:
var myTask = Task.Run(() => GetSteamIds(...));
And keep a reference to myTask somewhere so you can test myTask.IsCompleted to see when it's done.
Or, you can use ContinueWith to automatically run some other code when the Task completes. For example:
var myTask = Task.Run(() => GetSteamIds(...)).ContinueWith(() => {
//do something here now that the task is done
}, TaskScheduler.FromCurrentSynchronizationContext());
The TaskScheduler.FromCurrentSynchronizationContext() is to make sure that the continuation code runs on the UI thread (since you will likely want to update the UI with the results).
With this usage, there is no need for your method to be asynchronous at all (it does not need to return Task<>).
Or use BackgroundWorker, as suggested in the comments.

when you call await Task.Delay(1) it will actually give back access to the UI thread because probably the problem is not in the method you are calling, it is in the call of the method.
So you should remove the Task.Delay() in the method and show us the call of the method so we can help you further

Related

What thread runs a Task's continuation if you don't await the task?

I'm trying to wrap my head around control flow in C# when using async, Task, and await.
I understand how promises work, and that the returned Task<> from an async method will eventually contain the result of a computation/IO/whatever.
I think I understand that if you explicitly wait for that Task, then the current thread blocks until the Task is complete. I also think that means that the code in the async method that returns a Task will be running on a thread in a thread pool.
What I don't understand is what happens if I don't "await" the Task returned by an asynchronous method. It seems to me that the continuation is executed on the original thread that calls the async method, but I have no idea how control can return to that thread.
Here's an example. Here's I'm using UniTask which is basically Tasks for Unity:
public async UniTask ConnectAsync(Connection connection)
{
Debug.Log(Thread.CurrentThread.Name); -> this prints "Main Thread"
// Close Any Old Connections
await DisconnectAsync();
// Default Address
if (string.IsNullOrEmpty(connection.Address)) { connection.Address = "localhost:6379"; }
// Connect
ConfigurationOptions config = new()
{
EndPoints =
{
{ connection.Address, connection.Port },
},
User = connection.Username,
Password = connection.Password,
};
m_Connection = await ConnectionMultiplexer.ConnectAsync(config);
// Create Graph Client
m_Graph = new(m_Connection.GetDatabase());
// Notify
await Editor.Controller.OnConnect();
Debug.Log(Thread.CurrentThread.Name); -> this prints "Main Thread"
}
If I call this method, and then neglect to await the returned Task (UniTask), both Debug.Log() show that execution is happening on the "Main Thread" (i.e. the UI thread).
How is it that without awaiting this Task, the Main Thread is able to return to this continuation? Does C# wait until the thread is in the Suspended/WaitSleepJoin state? I'm not aware of any code putting the UI thread to sleep so I'm not sure about that. I'm certainly not putting the UI to sleep.
EDIT: I believe the chosen answer basically answered the question in the final sentence:
"Your code just needs to return to the main loop to allow the
continuation to run."
In other words, there's a loop somewhere deep in the bowels of (Unity in this case) and if the UI thread gets there, then it takes the opportunity to continue any pending tasks. (Please correct me in a comment if this is wrong and I'll update accordingly).
Incidentally, these links were very informative:
https://blog.stephencleary.com/2013/11/there-is-no-thread.html
https://www.ncameron.org/blog/async-io-fundamentals/
https://devblogs.microsoft.com/pfxteam/executioncontext-vs-synchronizationcontext/
https://blog.stephencleary.com/2012/02/async-and-await.html
What I don't understand is what happens if I don't "await" the Task returned by an asynchronous method. It seems to me that the continuation is executed on the original thread that calls the async method, but I have no idea how control can return to that thread.
As I describe on my blog, each await (by default) captures a "context", which is SynchronizationContext.Current or TaskScheduler.Current. In this particular case, the UI context is captured and used to resume the async method (i.e., execute the continuation).
How is it that without awaiting this Task, the Main Thread is able to return to this continuation? Does C# wait until the thread is in the Suspended/WaitSleepJoin state?
It has to do with contexts, not threads. The UI context schedules work by posting to the main UI message queue. So the continuation is run when the UI thread processes its message queue; it doesn't have anything to do with thread states.
I'm not aware of any code putting the UI thread to sleep so I'm not sure about that. I'm certainly not putting the UI to sleep.
Your code just needs to return to the main loop to allow the continuation to run.
I understand how promises work
Good, then we can stop right there. Tasks are nothing but compiler syntactic sugar over a promise. In fact, when JavaScript copied the await/async keywords from C#, they got implemented over the native Promise object.
Now, for the remainder of this I'm going to assume that you don't know how promises work. Think of it as getting called out on your promise bluff on your CV.
There's three parts to an async method:
The "synchronous" part. This is what will run when you simply call your async function, awaiting it or not, and is everything before the first await in your function. In your function this is the Debug.Log call and the synchronous part of DisconnectAsync.
The "asynchronous" part, the tail of your function. This gets stored as a lambda and it captures all necessary variables on creation. This gets called after #1 and when "done" it returns the Task object from your function. When the task is fully completed, the task is set as completed. Note that this can be recursive if you have multiple tails inside your tail.
All the magic of Task. For example, Task.WhenAll instantiates mutexes in your Task and then waits on them for completion. This makes Task technically disposable, and thus a memory and OS handle leak if you don't dispose every single task you create. await itself is handled through TaskCompletionSource, and you get just the task it manages. Things like that.
Note that nowhere in this did I mention threads. Tasks are to threads like what cats are to doctors. They both exist, some interact, but you have to be pretty insane to say cats are made to work only with doctors. Instead, tasks work on contexts. Thread pools are one default context. Another is single threaded contexts.
That's right, you can easily have async code run on a single thread, which is perfect for GUI in a single threaded render loop-driven game. You create a dialog, await its showing and get a result, all without any additional threads. You start an animation and await its completion, all without any threads.

How is concurrency handled when not using Task.Run() to schedule the work?

If we fill a list of Tasks that need to do both CPU-bound and I/O bound work, by simply passing their method declaration to that list (Not by creating a new task and manually scheduling it by using Task.Start), how exactly are these tasks handled?
I know that they are not done in parallel, but concurrently.
Does that mean that a single thread will move along them, and that single thread might not be the same thread in the thread pool, or the same thread that initially started waiting for them all to complete/added them to the list?
EDIT: My question is about how exactly these items are handled in the list concurrently - is the calling thread moving through them, or something else is going on?
Code for those that need code:
public async Task SomeFancyMethod(int i)
{
doCPUBoundWork(i);
await doIOBoundWork(i);
}
//Main thread
List<Task> someFancyTaskList = new List<Task>();
for (int i = 0; i< 10; i++)
someFancyTaskList.Add(SomeFancyMethod(i));
// Do various other things here --
// how are the items handled in the meantime?
await Task.WhenAll(someFancyTaskList);
Thank you.
Asynchronous methods always start running synchronously. The magic happens at the first await. When the await keyword sees an incomplete Task, it returns its own incomplete Task. If it sees a complete Task, execution continues synchronously.
So at this line:
someFancyTaskList.Add(SomeFancyMethod(i));
You're calling SomeFancyMethod(i), which will:
Run doCPUBoundWork(i) synchronously.
Run doIOBoundWork(i).
If doIOBoundWork(i) returns an incomplete Task, then the await in SomeFancyMethod will return its own incomplete Task.
Only then will the returned Task be added to your list and your loop will continue. So the CPU-bound work is happening sequentially (one after the other).
There is some more reading about this here: Control flow in async programs (C#)
As each I/O operation completes, the continuations of those tasks are scheduled. How those are done depends on the type of application - particularly, if there is a context that it needs to return to (desktop and ASP.NET do unless you specify ConfigureAwait(false), ASP.NET Core doesn't). So they might run sequentially on the same thread, or in parallel on ThreadPool threads.
If you want to immediately move the CPU-bound work to another thread to run that in parallel, you can use Task.Run:
someFancyTaskList.Add(Task.Run(() => SomeFancyMethod(i)));
If this is in a desktop application, then this would be wise, since you want to keep CPU-heavy work off of the UI thread. However, then you've lost your context in SomeFancyMethod, which may or may not matter to you. In a desktop app, you can always marshall calls back to the UI thread fairly easily.
I assume you don't mean passing their method declaration, but just invoking the method, like so:
var tasks = new Task[] { MethodAsync("foo"),
MethodAsync("bar") };
And we'll compare that to using Task.Run:
var tasks = new Task[] { Task.Run(() => MethodAsync("foo")),
Task.Run(() => MethodAsync("bar")) };
First, let's get the quick answer out of the way. The first variant will have lower or equal parallelism to the second variant. Parts of MethodAsync will run the caller thread in the first case, but not in the second case. How much this actually affects the parallelism depends entirely on the implementation of MethodAsync.
To get a bit deeper, we need to understand how async methods work. We have a method like:
async Task MethodAsync(string argument)
{
DoSomePreparationWork();
await WaitForIO();
await DoSomeOtherWork();
}
What happens when you call such a method? There is no magic. The method is a method like any other, just rewritten as a state machine (similar to how yield return works). It will run as any other method until it encounters the first await. At that point, it may or may not return a Task object. You may or may not await that Task object in the caller code. Ideally, your code should not depend on the difference. Just like yield return, await on a (non-completed!) task returns control to the caller of the method. Essentially, the contract is:
If you have CPU work to do, use my thread.
If whatever you do would mean the thread isn't going to use the CPU, return a promise of the result (a Task object) to the caller.
It allows you to maximize the ratio of what CPU work each thread is doing. If the asynchronous operation doesn't need the CPU, it will let the caller do something else. It doesn't inherently allow for parallelism, but it gives you the tools to do any kind of asynchronous operation, including parallel operations. One of the operations you can do is Task.Run, which is just another asynchronous method that returns a task, but which returns to the caller immediately.
So, the difference between:
MethodAsync("foo");
MethodAsync("bar");
and
Task.Run(() => MethodAsync("foo"));
Task.Run(() => MethodAsync("bar"));
is that the former will return (and continue to execute the next MethodAsync) after it reaches the first await on a non-completed task, while the latter will always return immediately.
You should usually decide based on your actual requirements:
Do you need to use the CPU efficiently and minimize context switching etc., or do you expect the async method to have negligible CPU work to do? Invoke the method directly.
Do you want to encourage parallelism or do you expect the async method to do interesting amounts of CPU work? Use Task.Run.
Here is your code rewritten without async/await, with old-school continuations instead. Hopefully it will make it easier to understand what's going on.
public Task CompoundMethodAsync(int i)
{
doCPUBoundWork(i);
return doIOBoundWorkAsync(i).ContinueWith(_ =>
{
doMoreCPUBoundWork(i);
});
}
// Main thread
var tasks = new List<Task>();
for (int i = 0; i < 10; i++)
{
Task task = CompoundMethodAsync(i);
tasks.Add(task);
}
// The doCPUBoundWork has already ran synchronously 10 times at this point
// Do various things while the compound tasks are progressing concurrently
Task.WhenAll(tasks).ContinueWith(_ =>
{
// The doIOBoundWorkAsync/doMoreCPUBoundWork have completed 10 times at this point
// Do various things after all compound tasks have been completed
});
// No code should exist here. Move everything inside the continuation above.

await Task.Run vs await

I've searched the web and seen a lot of questions regarding Task.Run vs await async, but there is this specific usage scenario where I don't not really understand the difference. Scenario is quite simple i believe.
await Task.Run(() => LongProcess());
vs
await LongProcess());
where LongProcess is a async method with a few asynchronous calls in it like calling db with await ExecuteReaderAsync() for instance.
Question:
Is there any difference between the two in this scenario? Any help or input appreciated, thanks!
Task.Run may post the operation to be processed at a different thread. That's the only difference.
This may be of use - for example, if LongProcess isn't truly asynchronous, it will make the caller return faster. But for a truly asynchronous method, there's no point in using Task.Run, and it may result in unnecessary waste.
Be careful, though, because the behaviour of Task.Run will change based on overload resolution. In your example, the Func<Task> overload will be chosen, which will (correctly) wait for LongProcess to finish. However, if a non-task-returning delegate was used, Task.Run will only wait for execution up to the first await (note that this is how TaskFactory.StartNew will always behave, so don't use that).
Quite often people think that async-await is done by several threads. In fact it is all done by one thread.
See the addition below about this one thread statement
The thing that helped me a lot to understand async-await is this interview with Eric Lippert about async-await. Somewhere in the middle he compares async await with a cook who has to wait for some water to boil. Instead of doing nothing, he looks around to see if there is still something else to do like slicing the onions. If that is finished, and the water still doesn't boil he checks if there is something else to do, and so forth until he has nothing to do but wait. In that case he returns to the first thing he waited for.
If your procedure calls an awaitable function, we are certain that somewhere in this awaitable function there is a call to an awaitable function, otherwise the function wouldn't be awaitable. In fact, your compiler will warn you if you forget to await somewhere in your awaitable function.
If your awaitable function calls the other awaitable function, then the thread enters this other function and starts doing the things in this function and goes deeper into other functions until he meets an await.
Instead of waiting for the results, the thread goes up in his call stack to see if there are other pieces of code he can process until he sees an await. Go up again in the call stack, process until await, etc. Once everyone is awaiting the thread looks for the bottom await and continues once that is finished.
This has the advantage, that if the caller of your awaitable function does not need the result of your function, but can do other things before the result is needed, these other things can be done by the thread instead of waiting inside your function.
A call without waiting immediately for the result would look like this:
private async Task MyFunction()
{
Task<ReturnType>taskA = SomeFunctionAsync(...)
// I don't need the result yet, I can do something else
DoSomethingElse();
// now I need the result of SomeFunctionAsync, await for it:
ReturnType result = await TaskA;
// now you can use object result
}
Note that in this scenario everything is done by one thread. As long as your thread has something to do he will be busy.
Addition. It is not true that only one thread is involved. Any thread who has nothing to do might continue processing your code after an await. If you check the thread id, you can see that this id can be changed after the await. The continuing thread has the same context as the original thread, so you can act as if it was the original thread. No need to check for InvokeRequired, no need to use mutexes or critical sections. For your code this is as if there is one thread involved.
The link to the article in the end of this answer explains a bit more about thread context
You'll see awaitable functions mainly where some other process has to do things, while your thread just has to wait idly until the other thing is finished. Examples are sending data over the internet, saving a file, communicating with a database etc.
However, sometimes some heavy calculations has to be done, and you want your thread to be free to do something else, like respond to user input. In that case you can start an awaitable action as if you called an async function.
Task<ResultType> LetSomeoneDoHeavyCalculations(...)
{
DoSomePreparations()
// start a different thread that does the heavy calculations:
var myTask = Task.Run( () => DoHeavyCalculations(...))
// now you are free to do other things
DoSomethingElse();
// once you need the result of the HeavyCalculations await for it
var myResult = await myTask;
// use myResult
...
}
Now a different thread is doing the heavy calculations while your thread is free to do other things. Once it starts awaiting your caller can do things until he starts awaiting. Effectively your thread will be fairly free to react on user input. However, this will only be the case if everyone is awaiting. While your thread is busy doing things your thread can't react on user input. Therefore always make sure that if you think your UI thread has to do some busy processing that takes some time use Task.Run and let another thread do it
Another article that helped me: Async-Await by the brilliant explainer Stephen Cleary
This answer deals with the specific case of awaiting an async method in the event handler of a GUI application. In this case the first approach has a significant advantage over the second. Before explaining why, lets rewrite the two approaches in a way that reflects clearly the context of this answer. What follows is only relevant for event handlers of GUI applications.
private async void Button1_Click(object sender, EventArgs args)
{
await Task.Run(async () => await LongProcessAsync());
}
vs
private async void Button1_Click(object sender, EventArgs args)
{
await LongProcessAsync();
}
I added the suffix Async in the method's name, to comply with the guidlines. I also made async the anonymous delegate, just for readability reasons. The overhead of creating a state machine is minuscule, and is dwarfed by the value of communicating clearly that this Task.Run returns a promise-style Task, not an old-school delegate Task intended for background processing of CPU-bound workloads.
The advantage of the first approach is that guarantees that the UI will remain responsive. The second approach offers no such guarantee. As long as you are using the build-in async APIs of the .NET platform, the probability of the UI being blocked by the second approach is pretty small. After all, these APIs are implemented by experts¹. By the moment you start awaiting your own async methods, all guarantees are off. Unless of course your first name is Stephen, and your surname is Toub or Cleary. If that's not the case, it is quite possible that sooner or later you'll write code like this:
public static async Task LongProcessAsync()
{
TeenyWeenyInitialization(); // Synchronous
await SomeBuildInAsyncMethod().ConfigureAwait(false); // Asynchronous
CalculateAndSave(); // Synchronous
}
The problem obviously is with the method TeenyWeenyInitialization(). This method is synchronous, and comes before the first await inside the body of the async method, so it won't be awaited. It will run synchronously every time you call the LongProcessAsync(). So if you follow the second approach (without Task.Run), the TeenyWeenyInitialization() will run on the UI thread.
How bad this can be? The initialization is teeny-weeny after all! Just a quick trip to the database to get a value, read the first line of a small text file, get a value from the registry. It's all over in a couple of milliseconds. At the time you wrote the program. In your PC. Before moving the data folder in a shared drive. Before the amount of data in the database became huge.
But you may get lucky and the TeenyWeenyInitialization() remains fast forever, what about the second synchronous method, the CalculateAndSave()? This one comes after an await that is configured to not capture the context, so it runs on a thread-pool thread. It should never run on the UI thread, right? Wrong. It depends to the Task returned by SomeBuildInAsyncMethod(). If the Task is completed, a thread switch will not occur, and the CalculateAndSave() will run on the same thread that called the method. If you follow the second approach, this will be the UI thread. You may never experience a case where the SomeBuildInAsyncMethod() returned a completed Task in your development environment, but the production environment may be different in ways difficult to predict.
Having an application that performs badly is unpleasant. Having an application that performs badly and freezes the UI is even worse. Do you really want to risk it? If you don't, please use always Task.Run(async inside your event handlers. Especially when awaiting methods you have coded yourself!
¹ Disclaimer, some built-in async APIs are not properly implemented.
Important: The Task.Run runs the supplied asynchronous delegate on a ThreadPool thread, so it's required that the LongProcessAsync has no affinity to the UI thread. If it involves interaction with UI controls, then the Task.Runis not an option. Thanks to #Zmaster for pointing out this important subtlety in the comments.

Async operation immediately awaited

I'm looking at these lines of code from another developer:
bool isValid = await engine.GetTaskByIdAsync(taskId);
if(isValid )
....
When I work with async operations, it is to do independent work while the async operation completes:
Task<bool> task = engine.GetTaskByIdAsync(taskId);
//Do work that doesn't need the "task" variable
bool completed = await task;
if(bool)
....
It appears that the first example kicks off an async operation and then immediately starts waiting. Is there some value here I don't understand?
Note: this code is in the data access layer of an application, so it isn't interacting with a user interface.
Is there some value here I don't understand?
Absolutely - it means that although you need the value returned by the operation before you can do any more work, you're not tying up a thread while you're waiting for it. That's particularly important if you're writing a GUI, where tying up the GUI thread basically means freezing the UI.
It sounds like you're focusing on the "doing multiple things in parallel" side of asynchrony, which is important, but far from the only benefit of it.
The commonly made mistake is that await blocks. It doesn't.
The current thread returns immediately and the remainder of the function is registered to be executed when the asynchronous Task completes.

When should you use Task.Run() rather than await?

I am storing the state of my data model. I clone the data model and then want to have it written to "disk" asynchronously.
Should I be using Task.Run() which runs it on a background thread?
Or should I just make it an async function and not await on it? (this will make it run on the UI thread)
Similar stuff to this, but my question is a bit different:
async Task.Run with MVVM
And what is the criteria to decide which to choose?
Thanks!
You should use Task.Run for CPU-based work that you want to run on a thread pool thread.
In your situation, you want to do I/O based work without blocking the UI, so Task.Run won't get you anything (unless you don't have asynchronous I/O APIs available).
As a side note, you definitely do want to await this work. This makes error handling much cleaner.
So, something like this should suffice:
async void buttonSaveClick(..)
{
buttonSave.Enabled = false;
try
{
await myModel.Clone().SaveAsync();
}
catch (Exception ex)
{
// Display error.
}
buttonSave.Enabled = true;
}
I don't think it matters. As far as I know, both methods get dispatched to a thread in the thread pool.
Using async will make the async method run on a background thread and continue on the thread that started it when the async method is finished. You can imagine the compiler seeing the await keyword, put the awaited method in a background thread and wiring up events to notice when the asynch method is finished. Therefore this might be the better choice if you want to show a UI change because of the successful save, and because this is lesser code of course.
Task.Run() would be nicer when you for any reason don't want to put the code in an async method, for example because you want the calling method itself not be async. Also, there's lesser event marshaling involved, but I heavily doubt that there's any performance difference at all.

Categories