Why is task.Wait not a dead end on a non-ui thread? Or am I just lucky?

First, some background information. I am in the process of creating existing C # library code suitable for execution on WinRT. Since the minor part of this code in depth should make a small I / O file, we first tried to synchronize things and used Task.Wait () to stop the main thread until all IOs were executed.

Of course, we quickly discovered what leads to a deadlock.

Then I found that I was changing a lot of code in the prototype to make it “asynchronous”. That is, I inserted async and expected keywords, and I changed the return method types accordingly. It was a lot of work - too much meaningless work actually, but I got a prototype that works this way.

Then I did an experiment, and I ran the source code using the Wait statement in a separate thread:

System.Threading.Tasks.Task.Run(()=> Draw(..., cancellationToken) 

No dead end!

Now I'm seriously confused because I thought I understood how asynchronous programming works. Our code does not yet use ConfigureAwait (false). Therefore, all pending statements should continue in the same context in which they were called. Right? I suggested that this means: the same stream. Now, if this thread called Wait, this should also lead to a deadlock. But this is not so.

Do any of you have a clear interpretation?

The answer to this question will determine if I really mess up our code by inserting a lot of conditional asynchronous / pending keywords, or I will keep it clean and just use the thread that executes Wait () here and there. If the continuations are triggered by an arbitrary non-blocking thread, everything should be fine. However, if they are started by the user interface thread, we may have problems if the continuation is costly.

I hope the problem is clear. If not, let me know.

+6
source share
1 answer

I have async / await intro on my blog where I will explain exactly what context is:

This is SynchronizationContext.Current , if it is not null , in which case it is TaskScheduler.Current . Note: if TaskScheduler no TaskScheduler current, then TaskScheduler.Current same as TaskScheduler.Default , which is a thread pool task scheduler.

In today's code, it usually comes down to whether you have a SynchronizationContext ; Task schedulers are not used today today (but are likely to become more common in the future). I have an article on the SynchronizationContext that describes how it works, and some of the implementations provided by .NET.

WinRT and other user interface interfaces (WinForms, WPF, Silverlight) provide a SynchronizationContext for the main user interface thread. This context is only one thread, so if you mix lock and asynchronous code, you can quickly see deadlocks. I describe why this happens in more detail in a blog post , but in general the reason it is deadlocked is because the async method tries to re-enter its SynchronizationContext (in this case, resume execution in the user interface thread), but the user interface thread The interface is blocked, waiting for the completion of this async method.

The thread pool does not have a SynchronizationContext (or TaskScheduler , usually). Therefore, if you execute a thread thread stream and block asynchronous code, it will not block. This is because the context captured is a thread pool context (which is not tied to a specific thread), so the async method can re-enter its context (just by working in a thread pool thread) while another thread pool thread is blocked waiting for its completion.

The answer to this question will determine if I really mess up our code by inserting a lot of conditional asynchronous / pending keywords, or I will keep it clean and just use the thread that executes Wait () here.

If your async code is complete, it should not look messy. I'm not sure what you mean by "conditional"; I would just do all this async . await has a "fast path" implementation that makes it synchronous if the operation is already completed.

Blocking asynchronous code using a background thread is possible, but it has some caveats:

  • You do not have a user interface context, so you cannot do many user interfaces.
  • You still have to “synchronize” with the user interface thread, and your user interface thread should not be blocked (for example, it should await Task.Run(..) , not Task.Run(..).Wait() ). This is especially true for WinRT applications.
+7
source

Source: https://habr.com/ru/post/951797/


All Articles