C# Waiting and Disposing of Tasks - c#

I have been looking at this article about using tasks in C#, and I was wandering if someone could clear something up for me? Doesn't calling wait on a task defeat the purpose of creating the task, because wouldn't it freeze up the main thread anyway because now the main thread has to wait for task to finish. I'm assuming however that wait won't be called straight away, so now when would you call it or how else would you know when to dispose of the task.
Assuming we have a simple case like this:
void MyFunction()
{
Task t = Task.Factory.StartNew(() => { System.Threading.Thread.Sleep(5000); });
}
if you call t.Wait() straight away the main thread will still wait 5 seconds before being able to do anything else, which kinda defeats the purpose from the users point of view, they wont be able to do anything for 5 seconds. How would you know that after 5 seconds that task has been completed? And you can dispose of t? What is a correct way of handling that? Sorry if the question is being really naive :(
Thanks All :D

You would probably never call Wait on a single task like this from a UI thread - as you said the whole point is not blocking the UI thread. In fact waiting on any task at all from the UI thread would be a problem.
Waiting for task completion might however be useful to synchronize multiple tasks and return a combined result - e.g. imagine two tasks that execute a hotel query on priceline and expedia concurrently and the thread that spawned both tasks (e.g. a background thread) waiting on the outcome of both and combining the results to order the available hotels on both sites by price.
The final result of the queries can then be dispatched back to the UI thread, typically by executing a callback or raising an event.

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 does SemaphoreSlim.WaitAsync allow for sequential executions of an async function?

The context for this question is a WPF application. WPF applications use a DispatcherSynchronizationContext.
If I have a button in my application that invokes the Button_Click handler method and I want to ensure all the code in that function is only ever executed by one thread I would wrap it in a semaphore as shown? But what I dont understand is how this works.
Assuming the button was clicked, we would hit WaitAsync() which returns a task that completes when the semaphore is entered, so I guess immediately? Then we would hit await GetLengthAsync() which would bounce us back out to the wpf message loop. Assuming 10 seconds goes by and the button is clicked again, then we would enter the Button_Click method again and hit WaitAsync(), which returns a task that completes when we enter the semaphore, and we cant enter the semaphore so we bounce back out to the message loop? is that how it works?
MAIN QUESTION -
Both times we hit WaitAsync() we are on the same thread, and our semaphore limits the concurrency to only allow one thread to execute that block of code at a time but it wont allow our same thread to enter that code either? the semaphore obviously cant be obtained by say some other threads like thread4 or thread5, but it also cant be obtained even by our same thread again? Any Clarification would be greatly appreciated.
private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1,1);
public async void Button_Click(object sender, EventArgs args)
{
await semaphoreSlim.WaitAsync();
try
{
// GetLengthAsync takes 40 seconds to complete
int length = await GetLengthAsync();
// LongComputeFunc takes 30 seconds to complete
int aggregate = LongComputeFunc(length);
}
finally
{
semaphoreSlim.Release();
}
}
Assuming the button was clicked, we would hit WaitAsync() which returns a task that completes when the semaphore is entered, so I guess immediately? Then we would hit await GetLengthAsync() which would bounce us back out to the wpf message loop.
Yes, and yes.
Assuming 10 seconds goes by and the button is clicked again, then we would enter the Button_Click method again and hit WaitAsync(), which returns a task that completes when we enter the semaphore, and we cant enter the semaphore so we bounce back out to the message loop? is that how it works?
Yes.
MAIN QUESTION - Both times we hit WaitAsync() we are on the same thread, and our semaphore limits the concurrency to only allow one thread to execute that block of code at a time but it wont allow our same thread to enter that code either? the semaphore obviously cant be obtained by say some other threads like thread4 or thread5, but it also cant be obtained even by our same thread again?
That is correct. Some synchronous coordination primitives do have the capability to allow recursive locks (e.g., Monitor), but others do not (e.g., Mutex). However, it is unnatural for asynchronous coordination primitives to support recursive locks. (Personally, I'm against recursive locking in general). Synchronous coordination primitives can get away with recursion because there is the notion of "a thread" that "owns" the lock. For asynchronous coordination primitives, there is no notion of a thread owning the lock; rather, a "block of code" owns the lock.
So, that's a long-winded way of saying that SemaphoreSlim.WaitAsync is not recursive (and shouldn't be).
Now, whether this is a good UX design or not is a different question. As noted in the comments, it's more common for UI to disable a button that represents a long-running operation if you only want one to be started at a time. That said, you could use the SemaphoreSlim approach if you want to allow the user to queue up more than one long-running operation. In this case, the SemaphoreSlim acts like a sort of implicit queue for your code.

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.

C# Async - How many objects are created here?

I am trying to understand the code I wrote,
for (int i = 0; i< 5; i++)
{
ExecuteCMD("/c robocopy C:\\Source D:\\Source /MIR", true);
}
public async void ExecuteCMD(string cmdLine, bool waitForExit)
{
await Task.Factory.StartNew(() =>
{
ExecuteProcess proc = new ExecuteProcess(cmdLine, waitForExit);
proc.Execute();
} );
}
The async method ExecuteCMD will run in a loop for 5 times. I know async doesn't create new threads. So, are there 5 objects created with the same name ('proc') in the same thread? Please explain
Many thanks in advance!
do you mean your ExecuteProcess proc Object? This is a local variable of your lambda function. So there is no conflict in your code.
The Lambda
() =>
{
ExecuteProcess proc = new ExecuteProcess(cmdLine, waitForExit);
proc.Execute();
}
is called 5 Times but every call creates only one instance of ExecuteProcess for the variable proc.
You are using Task.Factory.StartNew, so you will most likely (see Stephen Cleary's comment) end up on the default TaskScheduler, which happens to execute work on thread pool threads. Your ExecuteProcess allocation and Execute call will therefore occur 5 times, as expected, on thread pool threads (see above point re default scheduler) - and most likely in parallel to each other, and in parallel to your for loop (this last part might be difficult to wrap your head around, but that's the whole problem with async void - the execution order is non-deterministic; more on that later).
You are sort of right in that async/await does not necessarily create new threads. async/await is all about chaining tasks and their continuations so that they execute in correct order with respect to each other. Where the actual Task runs is determined by how that Task is created. Here you are explicitly requesting your work to be pushed out to the thread pool, because that is where Tasks created by Task.Factory.StartNew execute.
Others have pointed out that you might be using async void erroneously - possibly due to lack of understanding. async void is only good for scheduling work in a fire-and-forget manner, and this work needs to be self-contained, complete with its own exception handling and concurrency controls. Because your async void will run unobserved, in parallel to the rest of your code. It's like saying: "I want this piece of code to run at some point in the future. I don't need to know when it completes or whether it raised any exceptions - I'll just wait until it hits the first await, and then carry on executing my own work - and the rest of the async void will proceed on its own, in parallel, without supervision". Because of this, if you put some code after your for loop, it will most likely execute before your ExecuteProcess work, which may or may not be what you want.
Here's a step-by-step view of what actually happens in your application.
You hit the first iteration of the for loop on the main thread. The runtime calls ExecuteCMD, as if it were any other, synchronous method call. It enters the method and executes the code preceding the await, still as part of the first for loop iteration, on the main thread. Then it schedules some work on the thread pool via Task.Factory.StartNew. This work will execute at some point in the future on, let's say, thread pool thread #1. The Task returned by Task.Factory.StartNew is then awaited. This task cannot possibly complete synchronously, so the await schedules the rest of your async void to run in the future (on the main thread, after the task created by Task.Factory.StartNew has completed) and yields. At this point your ExecuteProcess work probably hasn't even started yet, but the main thread is already free to jump to the second iteration of the for loop. It does exactly that, which results in another task being scheduled to run on, say, thread pool thread #2, at some point in the future - followed by yet another continuation scheduled to run on the main thread. The for loop then jumps to the next item.
By the time your for loop ends, you will most likely have 5 Tasks waiting to execute on thread pool threads, followed by 5 continuation Tasks waiting to execute on the main thread. They will all complete at some point in the future. Your code won't know when because you told it that you don't care (by using async void).

Creating parallel tasks, how to limit new task creation?

In my program I'm starting parallel tasks one by one (http requests), doing it far ahead of any actual results those tasks can return.
It looks like
while (continueTaskSwarming == true)
{
Task.Run(() => Task());
}
Of course that way I have way more tasks scheduled than can be processed, so when I cancel them through cancellation token, it takes a lot of time to just cancel them all - they are scheduled after all, so they must actually run before they can be cancelled.
How can I organize task creation so that there always be only limited bunch of tasks ready to run, with new tasks scheduled only when there is some "place" for them? I mean, some "straight" way, something like
while (continueTaskSwarming == true)
{
PauseUntilScheduledTaskCountIsLowerThan(100);
Task.Run(() => Task());
}
Is that possible?
EDIT:
The obvious way, I think, is to put all tasks created into some list, removing them on completion and checking count of active/scheduled tasks. But I don't know (and don't know keywords to google it) if I can "remove" task from itself, or from ContinueWith() method. Anyone knows?
There are lots of ways to do this. You can protect a counter (of current tasks) with a lock, using Monitor.Wait() to have the task-producing thread wait until the counter is decremented and it's safe to start another task.
Another approach is to use the Semaphore synchronization object. This does basically the same thing, but encapsulated in an easy-to-use object. You create a new Semaphore, specifying how many concurrent threads you want to be able to hold the lock at once. By having each task take a lock on the semaphore, then you can have the task-producer also lock on the semaphore and when you read the max number of tasks, the producer won't be able to continue and create another task until one of the currently running tasks completes.
If I understand correctly, you are trying to achieve limited concurrency while executing tasks
this will help you
http://msdn.microsoft.com/en-us/library/ee789351(v=vs.110).aspx
Limited concurrency level task scheduler (with task priority) handling wrapped tasks

Categories