Confusion regarding threads, and if asynchronous methods are really asynchronous in C #

I read about async / wait and when Task.Yield could be useful and came across this post. I have a question regarding below this post:

When you use async / wait, there is no guarantee that the method you use the call when you expect FooAsync() will execute asynchronously. An internal implementation can freely return using a completely synchronous path.

This is a little incomprehensible to me, probably because the definition of asynchrony in my head does not line up.

In my opinion, since I mainly use UI dev, asynchronous code is code that does not run in the user interface thread, but in another thread. I assume that in the text I quoted, the method is not really asynchronous if it blocks any thread (even if it is a thread thread stream, for example).

Question:

If I have a lot of CPU-related work (let's say it does a lot of tough math), should this task asynchronously block some threads correctly? Something must actually do the math. If I wait for this, then the thread will be blocked.

What is an example of an asynchronous method and how will they work? Are they limited for I / O operations that use some hardware, so the thread is never blocked?

+45
c # asynchronous
May 15, '17 at 21:51
source share
7 answers

This is a little incomprehensible to me, probably because the definition of asynchrony in my head does not line up.

It’s good that you ask for clarification.

In my opinion, since I mainly use UI dev, asynchronous code is code that does not run in the user interface thread, but in another thread.

This belief is common, but false. There is no requirement that asynchronous code run on any second thread.

Imagine you are making breakfast. You put the toast on the toaster, and while you wait for the toast to pop, you look at the mail from yesterday, pay some bills, and hey, the toast popped up. You finish paying this bill, and then transfer it to a toast.

Where did you hire a second worker there to watch your toaster?

You have not done so. Themes are workers. Asynchronous workflows can run on a single thread. The point of an asynchronous workflow is to not hire more workers if you can avoid this.

If I have a lot of CPU-related work (let's say it does a lot of tough math), should this task asynchronously block some threads correctly? Something must really do the math.

Here I will give you a grave problem. Here is a column of 100 numbers; please add them manually. Thus, you add the first to the second and summarize. Then you add the total to the third and get the total. Then, oh hell, the second page of numbers is missing. Remember where you were and make toasts. Ah, when the toast was toast, the letter came with the rest of the numbers. When you finish lubricating the toasts, keep adding these numbers and remember to eat the toast the next time you have free time.

Where is the part where you hired another employee to add numbers? Computing costly work need not be synchronous and should not block the flow . What makes computing work potentially asynchronous is the ability to stop it, remember where you were, go to something else, remember what to do after that, and resume where you left off.

Now, of course, you can hire a second employee who does nothing but add numbers, and then get fired. And you might ask the worker, "Are you done?" and if the answer is no, you can go sandwich until they end. So you and the worker are busy. But there is no requirement that asynchrony include several workers.

If I wait for it, then the thread will be blocked.

NO NO NO. This is the most important part of your misunderstanding. await does not mean "start this work asynchronously." await means "I have an asynchronously received result that may not be available. If it is not available, find another work that will be performed on this thread so that we do not block the thread. opposite to what you just said.

What is an example of an asynchronous method and how will they work? Are they limited for I / O operations that use some hardware, so the thread is never blocked?

Asynchronous operation is often associated with custom hardware or multiple threads, but this is not necessary.

Do not think about workers. Think about workflows. The essence of asynchronization is to break the workflow into small parts so that you can determine the order in which these parts should be executed, and then execute each part in turn, but allow parts that have no dependencies with each other so that alternate.

In an asynchronous workflow, you can easily find places in the workflow where the relationship between the parts is expressed. Such parts are marked await . What is await : the following code depends on the completion of this part of the workflow, so if it is not completed, find another task and come back later when the task is completed. The thing is that the worker should work, even in a world where the necessary results are created in the future.

+100
May 15 '17 at 23:40
source share

I read about async / wait

Can I recommend async intro ?

and when Task.Yield can be useful

Almost never. I find this sometimes useful in unit testing.

In my opinion, since I mainly use UI dev, asynchronous code is code that does not run in the user interface thread, but in another thread.

Asynchronous code can be threadless .

I assume that in the text I quoted, the method is not really asynchronous if it blocks any thread (even if it is a thread thread stream, for example).

I would say that right. I use the term “truly asynchronous” for operations that do not block threads (and which are not synchronous). I also use the term “fake asynchronous” for operations that appear asynchronously but only work because they start or block the thread pool thread.

If I have a lot of CPU-related work (let's say it does a lot of tough math), should this task asynchronously block some threads correctly? Something must really do the math.

Yes; in this case, you would like to determine what works with the synchronous API (since it is synchronous work), and then you can call it from the user interface thread using Task.Run , for example:

 var result = await Task.Run(() => MySynchronousCpuBoundCode()); 

If I wait for it, then the thread will be blocked.

No; the thread pool thread will be used to run the code (not actually blocked), and the user interface thread asynchronously waits for the code to finish (also not blocked).

What is an example of an asynchronous method and how will they work?

NetworkStream.WriteAsync (indirectly) asks the network card to write a few bytes. There is no thread responsible for writing bytes one at a time and waiting for each byte to be written. The network card handles all this. When the network card writes all bytes, it (eventually) completes the task returned from WriteAsync .

Are they limited to I / O operations that use some hardware, so the thread is never blocked?

Not really, although I / O operations are easy examples. Another fairly simple example is timers (e.g. Task.Delay ). Although you can create a truly asynchronous API for any type of event.

+26
May 16 '17 at 12:03 a.m.
source share

When you use async / wait, there is no guarantee that the method you call when you expect FooAsync () will actually execute asynchronously. An internal implementation can be returned using a fully synchronous path.

This is a little incomprehensible to me, probably because the definition of asynchronous in my head does not line up.

It just means that there are two cases when calling the async method.

Firstly, after you return the task, the operation is already completed - it will be a synchronous path. Secondly, the operation is still ongoing - this is an asynchronous path.

Consider this code, which should show both of these paths. If the key is in the cache, it is returned synchronously. Otherwise, async op starts up, which calls the database:

 Task<T> GetCachedDataAsync(string key) { if(cache.TryGetvalue(key, out T value)) { return Task.FromResult(value); // synchronous: no awaits here. } // start a fully async op. return GetDataImpl(); async Task<T> GetDataImpl() { value = await database.GetValueAsync(key); cache[key] = value; return value; } } 

So, understanding this, you can conclude that theoretically calling database.GetValueAsync() can have similar code and can return synchronously: so even your async path can end 100% synchronously. But your code does not need to be taken care of: async / await handles both cases without problems.

If I have a lot of CPU-related work (let's say it does a lot of tough math), should this task asynchronously block some threads correctly? Something must actually do the math. If I wait for this, then the thread will be blocked.

Blocking is a clearly defined term - it means that your thread has given its execution window while it is waiting for something (I / O, mutex, etc.). Thus, your math thread is not considered blocked: it actually does the work.

What is an example of an asynchronous method and how will they work? Are they limited for I / O operations that use some hardware, so the thread is never blocked?

A truly asynchronous method will be one that simply never blocks. Usually this ends with I / O, but it can also mean await your heavy math code when you want to use the current thread for something else (as in UI design) or when you try to enter parallelism:

 async Task<double> DoSomethingAsync() { double x = await ReadXFromFile(); Task<double> a = LongMathCodeA(x); Task<double> b = LongMathCodeB(x); await Task.WhenAll(a, b); return a.Result + b.Result; } 
+10
May 15, '17 at 22:27
source share

This topic is quite extensive, and several discussions may arise. However, using async and await in C # is considered asynchronous programming. However, how asynchrony works is a general discussion. Prior to .NET 4.5, there were no asynchronous and pending keywords, and developers had to develop directly against Task Parallel Librar y (TPL). There, the developer fully controlled when and how to create new tasks and even threads. However, this had a drawback, since it was not really an expert on this topic, applications can suffer from severe performance problems and errors due to race conditions between threads and so on.

As of .NET 4.5, async keywords and expectations have been introduced with a new approach to asynchronous programming. Asynchronous and pending keywords do not create additional threads. Asynchronous methods do not require multithreading, because the async method does not start in its thread. The method works in the current synchronization context and uses the time in the stream only when the method is active. You can use Task.Run to move the processor-related work to the background thread, but the background thread does not help with the process, which just waits for the results to become available.

An asynchronous approach to asynchronous programming is preferable to existing approaches in almost every case. In particular, this approach is better than BackgroundWorker for IO-bound operations, because the code is simpler, and you do not need to protect it from race conditions. You can learn more about this topic HERE .

I do not consider myself a C # black belt, and some more experienced developers can raise some further discussions, but in principle I hope that I will be able to answer your question.

+6
May 15 '17 at 22:14
source share

Asynchronous Doesn't Assume Parallel

Asynchronous only means concurrency. In fact, even using explicit threads does not guarantee that they will be executed at the same time (for example, when thread merges for the same core or, more often, when there is only one core in the machine).

Therefore, you should not expect that the asynchronous operation will be performed simultaneously with something else. Asynchronous means that this will happen, after all, at another time (a (Greek) = without, syn (Greek) = together, khronos (Greek) = time. => Asynchronous = does not happen at the same time).

Note. The idea of ​​asynchrony is that when you call you don’t care when the code really runs. This allows the system to use parallelism, if possible, to perform the operation. It can even start immediately. This can happen even in the same topic ... more on this later.

When you await do an asynchronous operation, you create concurrency (com (latin) = together, currere (latin) = run. => "Concurrent" = to run together). This is because you are asking the asynchronous operation to complete before moving on. We can say that the execution is converging. This is similar to the concept of thread pooling.




If asynchronous cannot be parallel

When you use async / wait, there is no guarantee that the method you call when you expect FooAsync () will actually execute asynchronously. An internal implementation can be returned using a fully synchronous path.

This can happen in three ways:

  • You can use await for everything that Task returns. When you receive the Task , it may already be completed.

    However, this alone does not mean that it worked synchronously. In fact, this assumes that it starts asynchronously and is complete before you receive an instance of Task .

    Remember that you can await to complete an already completed task:

     private static async Task CallFooAsync() { await FooAsync(); } private static Task FooAsync() { return Task.CompletedTask; } private static void Main() { CallFooAsync().Wait(); } 

    Also, if the async method does not have await , it will work synchronously.

    Note. As you already know, the method that Task returns may be waiting on the network or in the file system, etc ... this does not mean that you are starting a new Thread or putting something on ThreadPool .

  • In the context of synchronization, which is processed by a single thread, the result will be the execution of the Task synchronously with some overhead. This concerns the user interface flow, I will talk more about what happens below.

  • You can write a custom TaskScheduler to always run tasks synchronously. In the same thread that makes the call.

    Note. I recently wrote a custom SyncrhonizationContext that runs tasks in a single thread. You can find it in Creating a Task Scheduler (System.Threading.Tasks.) . This will result in such a TaskScheduler with a call to FromCurrentSynchronizationContext .

    By default, TaskScheduler will call ThreadPool calls. However, when you wait from an operation if it was not running on ThreadPool , it will try to remove it from ThreadPool and start it inline (in the same thread that is waiting ... the thread is still waiting, so it is not busy).

    Note. One notable exception is Task , noted by LongRunning . LongRunning Task will work in a separate thread .




Your question

If I have a lot of CPU-related work (let's say it does a lot of tough math), should this task asynchronously block some threads correctly? Something must actually do the math. If I wait for this, then the thread will be blocked.

If you perform calculations, they must happen in some thread, this part of the right.

However, the beauty of async and await is that the waiting thread should not be blocked (more on this later). However, it is very easy to shoot in the leg, expecting the expected task to be launched in the same thread that is waiting, which will lead to synchronous execution (which is an easy mistake in the user interface thread).

One of the key characteristics of async and await is that they accept a SynchronizationContext from the caller. For most threads that lead to the use of the standard TaskScheduler (which, as mentioned earlier, uses ThreasPool ). However, for the user interface thread, this means that tasks are sent to the message queue, which means that they will be executed in the user interface thread. The advantage of this is that you do not need to use Invoke or BeginInvoke to access the user interface components.

Before moving on to await a Task from a user interface thread without blocking it, I want to note that you can implement TaskScheduler , if you are await on a Task , you don’t lock your thread or stand idle, instead you let your thread select another Task that is waiting to be executed . When I was backporting Tasks for.NET 2.0 I experimented with this.

What is an example of an asynchronous method and how will they work? Are they limited for I / O operations that use some hardware, so the thread is never blocked?

It seems you got confused asynchronously without blocking the thread. , , .NET, , , , - await . , , TaskScheduler.FromCurrentSynchronizationContext .

. Timer , Application.Idle - .

async , , . . await , Task , async ( ). Task , ( await ).

, , await , . Task , ( await ) . await .

async await , .

. Windows Forms, Button TextBox :

  private async void button1_Click(object sender, EventArgs e) { await WorkAsync(5000); textBox1.Text = @"DONE"; } private async Task WorkAsync(int milliseconds) { Thread.Sleep(milliseconds); } 

. , , , await SynchronizationContext . . WorkAsync .

Here's what happens:

  • await WorkAsync(5000)
  • WorkAsync(5000) ( ) , ... , .
  • .
  • WorkAsync(5000)
  • WorkAsync(5000)
  • WorkAsync Thread.Sleep . 5 .
  • , .
  • .

.

, Task.Delay . ; Sleep . , async await , . , (, ThreadPool ) .

:

  private async void button1_Click(object sender, EventArgs e) { await Task.Run(() => Work(5000)); textBox1.Text = @"DONE"; } private void Work(int milliseconds) { Thread.Sleep(milliseconds); } 

, . , Thread.Sleep ThreadPool Task.Run . button1_Click async , await , . Task await , , .

Here's what happens:

  • await Task.Run(() => Work(5000))
  • Task.Run(() => Work(5000)) ( ) , ... , .
  • .
  • Task.Run(() => Work(5000)) ,
  • Task.Run(() => Work(5000)) , ThreadPool
  • .

ThreadPool , , . , .

+6
16 '17 12:55
source share

, ; Qaru ( ).

: . , - ​​ - . It is very important.

await . 1 .

 var a = await A(); await B(a); 

await invocations. B A, , A B. , , , A B .

, , , , , , . B A, A 2 . HTTP- URL- HTTP-, ; /. , " ", " ", , .

You speak:

, UI dev, - , , .

, . , , ( , ). - . , - , (, , ), ( "" "" ). await Application.DoEvents - .

, . . , await . , , await , ? No.

-, , await , . A , , await , , " ". A Task<T> T , " " - await - , ( ) ( , , ). Task<T> A .

:

 public async Task<int> A() { Thread.Sleep(1000); return 42; } 

A , ( int); await , return 42; . , . , , await - A() , await theTaskResultOfA .

, :

 public async Task<int> A() { await Task.Delay(1000); return 42; } 

await , , ; await , , . . , .

: . - , , . , - , , :

 var responseTask = GetAsync("http://www.google.com"); // Do some CPU intensive task ComputeAllTheFuzz(); response = await responseTask; 

- . ( ComputeAllTheFuzz HTTP-) . - (, -, ComputeAllTheFuzz , HTTP-). await , ( , - Task.WhenAll ). , HTTP- , await - . - ; . .

, , . ( ), ( ). , , . -, , - , ( ) - ).

, await , . , , . , .

UI. (, -), ( ). /, , . , ; (awaiter ConfigureAwait(false) ), UI . , , . , , , - , " , " async Task.Run . - , :)




+4
16 '17 9:48
source share

, , async/await , , .

 public static async Task<string> Foo() { Console.WriteLine("In Foo"); await Task.Yield(); Console.WriteLine("I'm Back"); return "Foo"; } static void Main(string[] args) { var t = new Task(async () => { Console.WriteLine("Start"); var f = Foo(); Console.WriteLine("After Foo"); var r = await f; Console.WriteLine(r); }); t.RunSynchronously(); Console.ReadLine(); } 

enter image description here

, , async/await ( )

NOTE. :)

, "", , - . , , async/await .

, . , . , , , .. . , , (//), - , (), .

+1
15 '17 23:37
source share



All Articles