-
Notifications
You must be signed in to change notification settings - Fork 0
C# Tips & Tricks
Threads execute Tasks which are scheduled by a TaskScheduler.
Task represents some work that needs to be done. A Task may or may not be completed. The moment when it completes can be right now or in the future.
Tasks have nothing to do with Threads and this is the cause of many misconceptions, Task is not thread. Task does not guarantee parallel execution. Task does not belong to a Thread or anything like that. They are two separate concepts and should be treated as such.
If the Task is completed and not faulted then the continuation task will be scheduled. Faulted state means that there was an exception. Tasks have an associated TaskScheduler which is used to schedule a continuation Task, or any other child Tasks that are required by the current Task.
Threads just as in any OS represent execution of code. Threads keep track what you execute and where you execute. Threads have a call stack, store local variables, and the address of the currently executing instruction. In C# each thread also has an associated SynchronizationContext which is used to communicate between different types of threads
When you use async/await, there is no guarantee that the method you call when you do await FooAsync() will actually run asynchronously. The internal implementation is free to return using a completely synchronous path.
If you're making an API where it's critical that you don't block and you run some code asynchronously, and there's a chance that the called method will run synchronously (effectively blocking), using await Task.Yield() will force your method to be asynchronous, and return control at that point. The rest of the code will execute at a later time (at which point, it still may run synchronously) on the current context.
This can also be useful if you make an asynchronous method that requires some "long running" initialization, ie:
private async void button_Click(object sender, EventArgs e) { await Task.Yield(); // Make us async right away
var data = ExecuteFooOnUIThread(); // This will run on the UI thread at some point later
await UseDataAsync(data);
}
Without the Task.Yield() call, the method will execute synchronously all the way up to the first call to await.
The code example should be obvious what it does if you are at least a bit familiar with async/await. The request is done asynchronously and the thread is free to work on other tasks while the server responds. This is the ideal case.
Another way to perform
Everything you do with async and await end up in an execution queue. Each Task is queued up using a TaskScheduler which can do anything it wants with your Task. This is where things get interesting, the TaskScheduler depends on context you are currently in.