简体   繁体   English

等待异步方法在后台做什么?

[英]What does awaiting an asynchronous method do in background?

I've red various articles about async await and i'm trying to understand the await async in depth. 我已经发布了关于异步等待的各种文章,我试图理解等待异步的深度。 My problem is that i found out that awaiting an asyncronous method doesn't creatE a new thread, it rather just make the UI responsive. 我的问题是,我发现等待异步方法不会创建一个新线程,而只是让UI响应。 If it's like that there's no time gain when using await async since no extra thread is used. 如果它是这样的,那么在使用等待异步时没有时间增益,因为没有使用额外的线程。

What i knew so far is that only Task.Run() create a new thread. 到目前为止我所知道的是只有Task.Run()创建一个新线程。 Is this also true for Task.WhenAll() or Task.WhenAny() ? 对于Task.WhenAll()或Task.WhenAny(),这也是如此吗?

Let's say we have this code : 假设我们有这个代码:

    async Task<int> AccessTheWebAsync()
            {
                using (HttpClient client = new HttpClient())
                {
                    Task<string> getStringTask = client.GetStringAsync("https://docs.microsoft.com");

                    DoIndependentWork();

                    string urlContents = await getStringTask;

                    return urlContents.Length;
                }
            }

What i expect : 我期待的是:

  1. When creating the getStringTask Task, another thread will copy the current context and start executing the GetStringAsync method. 创建getStringTask任务时,另一个线程将复制当前上下文并开始执行GetStringAsync方法。

  2. When awaiting getStringTask, we will see if the other thread has completed his task, if not the control will be back the caller of AccessTheWebAsync() method until the other thread complets it's task to resume the control. 在等待getStringTask时,我们将看到其他线程是否已完成其任务,如果不是,则控件将返回AccessTheWebAsync()方法的调用者,直到另一个线程完成其恢复控制的任务为止。

So i really don't get how no extra thread is created when awaiting a Task. 所以我真的不知道在等待任务时如何创建额外的线程。 Can someone explain what's exactly happening when awaiting a Task ? 有人可以解释在等待任务时究竟发生了什么吗?

I've red various articles about async await and i'm trying to understand the await async in depth. 我已经发布了关于异步等待的各种文章,我试图理解等待异步的深度。

A noble pursuit. 一种崇高的追求。

My problem is that i found out that awaiting an asyncronous method doesn't creat a new thread, it rather just make the UI responsive. 我的问题是,我发现等待异步方法不会创建新线程,而只是让UI响应。

Correct. 正确。 It is very important to realize that await means asynchronous wait . 认识到await意味着异步等待是非常重要的。 It does not mean "make this operation asynchronous". 并不意味着“使这个异步操作”。 It means: 它的意思是:

  • This operation is already asynchronous . 此操作已异步
  • If the operation is complete, fetch its result 如果操作完成,则获取其结果
  • If the operation is not complete, return to the caller and assign the remainder of this workflow as the continuation of the incomplete operation. 如果操作未完成,请返回调用方并将此工作流的其余部分指定为不完整操作的继续。
  • When the incomplete operation becomes complete, it will schedule the continuation to execute. 当不完整的操作完成时,它将安排继续执行。

If it's like that there's no time gain when using await async since no extra thread is used. 如果它是这样的,那么在使用等待异步时没有时间增益,因为没有使用额外的线程。

This is incorrect. 这是不正确的。 You're not thinking about the time win correctly. 你没有想到正确赢得时间。

Imagine this scenario. 想象一下这个场景。

  • Imagine a world with no ATMs. 想象一个没有ATM的世界。 I grew up in that world. 我在那个世界长大。 It was a strange time. 这是一个奇怪的时刻。 So there is usually a line of people at the bank waiting to deposit or withdraw money. 因此,银行通常有一排人在等待存款或取款。
  • Imagine there is only one teller at this bank. 想象一下,这家银行只有一位出纳员。
  • Now imagine that the bank only takes and gives out single dollar bills. 现在想象一下,银行只接受并发出单一美元钞票。

Suppose there are three people in line and they each want ten dollars. 假设有三个人排队,他们每个人都想要10美元。 You join the end of the line, and you only want one dollar. 你加入了行的末尾,你只想要一美元​​。 Here are two algorithms: 这是两个算法:

  • Give the first person in the line one dollar. 给第一个人一美元。
  • [ do that ten times ] [这十次]
  • Give the second person in the line one dollar. 给第二个人一美元。
  • [ do that ten times ] [这十次]
  • Give the third person in the line one dollar. 给第三个人一美元。
  • [ do that ten times ] [这十次]
  • Give you your dollar. 给你你的美元。

How long does everyone have to wait to get all their money? 每个人都要等多久才能得到所有的钱?

  • Person one waits 10 time units 人一等待10个单位
  • Person two waits 20 人二等20
  • Person three waits 30 人三等30
  • You wait 31. 你等31。

That's a synchronous algorithm. 那是一个同步算法。 An asynchronous algorithm is: 异步算法是:

  • Give the first person in the line one dollar. 给第一个人一美元。
  • Give the second person in the line one dollar. 给第二个人一美元。
  • Give the third person in the line one dollar. 给第三个人一美元。
  • Give you your dollar. 给你你的美元。
  • Give the first person in the line one dollar. 给第一个人一美元。
  • ... ...

That's an asynchronous solution. 这是一个异步解决方案。 Now how long does everyone wait? 现在每个人都等了多久?

  • Everyone getting ten dollars waits about 30. 得到10美元的每个人等待大约30美元。
  • You wait 4 units. 你等4个单位。

The average throughput for large jobs is lower, but the average throughput for small jobs is much higher . 大型工作的平均吞吐量较低,但小型工作的平均吞吐量要高得多 That's the win. 这就是胜利。 Also, the time-to-first-dollar for everyone is lower in the asynchronous workflow, even if the time to last dollar is higher for big jobs. 此外,异步工作流程中每个人的首次购买时间较低,即使大型工作的持续时间较长。 Also, the asynchronous system is fair ; 此外,异步系统是公平的 ; every job waits approximately (size of job)x(number of jobs). 每个工作大约等于(工作的大小)x(工作的数量)。 In the synchronous system, some jobs wait almost no time and some wait a really long time. 在同步系统中,一些工作几乎没有时间等待,有些工作等待很长时间。

The other win is: tellers are expensive; 另一个胜利是:出纳员很贵; this system hires a single teller and gets good throughput for small jobs. 该系统雇用单个出纳员,并为小型工作获得良好的吞吐量。 To get good throughput in the synchronous system, as you note, you need to hire more tellers which is expensive. 为了在同步系统中获得良好的吞吐量,正如您所注意到的,您需要雇用更多昂贵的柜员。

Is this also true for Task.WhenAll() or Task.WhenAny() ? 对于Task.WhenAll()或Task.WhenAny(),这也是如此吗?

They do not create threads. 他们不创建线程。 They just take a bunch of tasks and complete when all/any of the tasks are done. 他们只需完成一系列任务,并在完成所有/任何任务后完成任务。

When creating the getStringTask Task, another thread will copy the current context and start executing the GetStringAsync method. 创建getStringTask任务时,另一个线程将复制当前上下文并开始执行GetStringAsync方法。

Absolutely not. 绝对不。 The task is already asynchronous and since it is an IO task it doesn't need a thread. 该任务已经异步的 ,因为它是一个IO任务,所以它不需要一个线程。 The IO hardware is already asynchronous. IO硬件已经异步。 There is no new worker hired. 没有招聘新员工。

When awaiting getStringTask, we will see if the other thread has completed his task 等待getStringTask时,我们将看到其他线程是否已完成其任务

No, there is no other thread. 不,没有其他线程。 We see if the IO hardware has completed its task. 我们看看IO硬件是否已完成其任务。 There is no thread. 没有线程。

When you put a piece of bread in the toaster, and then go check your email, there is no person in the toaster running the toaster . 当你把一块面包放在烤面包机里,然后检查你的电子邮件时, 烤面包机里没有人在运行烤面包机 The fact that you can start an asynchronous job and then go off and do other stuff while it is working is because you have special purpose hardware that is by its nature asynchronous . 您可以启动异步作业然后在其工作时执行其他操作的事实是因为您具有本质异步的特殊用途硬件 That's true of network hardware the same way it is true of toasters. 网络硬件的确如此,就像烤面包机一样。 There is no thread . 没有线程 There is no tiny person running your toaster. 没有小人在运行烤面包机。 It runs itself. 它自己运行。

if not the control will be back to the caller of AccessTheWebAsync() method until the other thread completes its task to resume the control. 如果不是,控件将返回到AccessTheWebAsync()方法的调用者,直到另一个线程完成其任务以恢复控制。

Again, there is no other thread. 同样,没有其他线程。

But the control flow is correct. 但控制流程是正确的。 If the task is complete then the value of the task is fetched. 如果任务完成,则获取任务的值。 If it is not complete then control returns to the caller, after assigning the remainder of the current workflow as the continuation of the task. 如果未完成,则在将当前工作流的剩余部分指定为任务的延续之后,控制将返回到调用者。 When the task is complete, the continuation is scheduled to run. 任务完成后,计划继续运行。

i really don't get how no extra thread is created when awaiting a Task. 我真的不明白在等待任务时如何创建额外的线程。

Again, think about every time in your life when you stopped doing a task because you were blocked, did something else for a while, and then started up doing the first task again when you got unblocked. 再一次,想想你生命中的每一次当你停止做任务因为你被阻挡,做了一段时间的其他事情,然后在你被解锁时再次开始做第一个任务。 Did you have to hire a worker? 你是否需要雇佣一名工人? Of course not. 当然不是。 Yet somehow you managed to make eggs while the toast was in the toaster. 然而不知何故,当吐司在烤面包机中时,你设法制作鸡蛋。 Task based asynchrony just puts that real-world workflow into software. 基于任务的异步只是将现实工作流程放入软件中。

It never ceases to amaze me how you kids today with your weird music act like threads always existed and there is no other way to do multitasking . 它永远不会让我惊讶你今天的孩子你的奇怪的音乐行为像线程一直存在, 没有其他方式做多任务处理 I learned how to program in an operating system that didn't have threads. 我学会了如何在一个操作系统, 没有线程编程。 If you wanted two things to appear to happen at the same time, you had to build your own asynchrony; 如果你想要同时发生两件事,你必须建立自己的异步; it wasn't built into the language or the OS. 它没有内置于语言或操作系统中。 Yet we managed. 然而我们成功了。

Cooperative single-threaded asynchrony is a return to the world as it was before we made the mistake of introducing threads as a control flow structure; 合作单线程异步是一种回归世界,就像我们在将线程作为控制流结构引入错误之前一样; a more elegant and far simpler world. 一个更优雅,更简单的世界。 An await is a suspension point in a cooperative multitasking system. 等待是合作多任务系统中的暂停点。 In pre-threading Windows, you'd call Yield() for that, and we didn't have language support for creating continuations and closures; 在预先线程化Windows中,你会为此调用Yield() ,而我们没有语言支持来创建continuation和闭包。 you wanted state to persist across a yield, you wrote the code to do it. 你希望状态坚持收益,你编写了代码来做到这一点。 You all have it easy! 你们都很容易!

Can someone explain what exactly happening when awaiting a Task ? 有人可以解释在等待任务时究竟发生了什么吗?

Exactly what you said, just with no thread. 正是你所说的,只是没有线程。 Check to see if the task is done; 检查任务是否完成; if it's done, you're done. 如果它完成了,你就完成了。 If not, schedule the remainder of the workflow as the continuation of the task, and return. 如果没有,请将工作流的其余部分安排为任务的继续,然后返回。 That's all await does. 这一切都await

I just want to confirm something. 我只想确认一些事情。 Is it always the case that there's no thread created when awaiting a task? 在等待任务时是否始终没有创建线程?

We worried when designing the feature that people would believe, as you still might, that "await" does something to the call which comes after it. 我们担心在设计一个人们会相信的功能时,就像你仍然可能的那样,“等待”会对它之后的呼叫产生影响。 It does not . 它没有 Await does something to the return value . Await对返回值做了一些事情。 Again, when you see: 再次,当你看到:

int foo = await FooAsync();

you should mentally see: 你应该在精神上看到:

Task<int> task = FooAsync();
if (task is not already completed) 
   set continuation of task to go to "resume" on completion
   return;
resume: // If we get here, task is completed
int foo = task.Result;

A call to a method with an await is not a special kind of call. 使用await调用方法不是一种特殊的调用。 The "await" does not spin up a thread, or anything like that. “等待”不会启动一个线程,或类似的东西。 It is an operator that operates on the value that was returned. 它是一个运算符,它根据返回的值进行操作。

So awaiting a task does not spin up a thread. 所以等待任务不会启动一个线程。 Awaiting a task (1) checks to see if the task is complete, and (2) if it is not, assigns the remainder of the method as the continuation of the task, and returns. 等待任务(1)检查任务是否完成,以及(2)如果不是,则将方法的其余部分指定为任务的继续,然后返回。 That's all. 就这样。 Await does not do anything to create a thread. Await没有做任何事情来创建一个线程。 Now, maybe the called method spins up a thread; 现在,也许被调用的方法会旋转一个线程; that's it's business . 这是它的业务 That has nothing to do with the await , because the await doesn't happen until after the call returns. 这与await无关,因为await直到调用返回后才会发生。 The called function does not know its return value is being awaited . 被调用的函数不知道正在等待它的返回值

Let's say we await a CPU bound task that does heavy calculations. 假设我们等待进行大量计算的CPU绑定任务。 What i know so far is a I/O bound code it will be executed on low level CPU components (much lower than threads) and only use a thread briefly to notify the context about the finished Task status. 到目前为止我所知道的是一个I / O绑定代码,它将在低级CPU组件上执行(远低于线程),并且只是简单地使用一个线程来通知上下文关于完成的任务状态。

What we know about the call to FooAsync above is that it is asynchronous, and it returns a task. 我们对上面调用FooAsync的了解是它是异步的,它返回一个任务。 We do not know how it is asynchronous. 我们不知道它是如何异步的。 That's the author of FooAsync's business! 这是FooAsync业务的作者! But there are three main techniques that the author of FooAsync can use to achieve asynchrony. 但是,FooAsync的作者可以使用三种主要技术来实现异步。 As you note, the two main techniques are: 如您所知,两种主要技术是:

  • If the task is high-latency because it requires a long computation to be done on the current machine on another CPU, then it makes sense to obtain a worker thread and start the thread doing the work on another CPU. 如果任务是高延迟的,因为它需要在另一个CPU上的当前机器上进行长计算,那么获取工作线程并启动线程在另一个CPU上执行工作是有意义的。 When the work is finished, the associated task can schedule its continuation to run back on the UI thread, if the task was created on the UI thread, or on another worker thread, as appropriate. 当工作完成时,相关任务可以安排其继续在UI线程上运行,如果任务是在UI线程上创建的,或者在适当时在另一个工作线程上创建。

  • If the task is high-latency because it requires communication with slow hardware, like disks or networks, then as you note, there is no thread. 如果任务是高延迟的,因为它需要与缓慢的硬件(如磁盘或网络)进行通信,那么正如您所注意到的那样,没有线程。 Special-purpose hardware does the task asynchronously and the interrupt handling provided by the operating system ultimately takes care of getting the task completion scheduled on the right thread. 专用硬件异步执行任务,操作系统提供的中断处理最终负责在正确的线程上安排任务完成。

  • A third reason to be asynchronous is not because you're managing a high-latency operation, but because you're breaking up an algorithm into little parts and putting them on a work queue. 异步的第三个原因不是因为你正在管理一个高延迟的操作,而是因为你将算法分成几个部分并将它们放在一个工作队列上。 Maybe you're making your own custom scheduler, or implementing an actor model system, or trying to do stackless programming, or whatever. 也许你正在制作自己的自定义调度程序,或者实现一个actor模型系统,或者尝试进行无堆栈编程,或者其他什么。 There's no thread, there's no IO, but there is asynchrony. 没有线程,没有IO,但存在异步。

So, again, awaiting does not make something run on a worker thread. 所以,再次, 等待不会在工作线程上运行。 Calling a method that starts a worker thread makes something run on a worker thread . 调用启动工作线程的方法可以在工作线程上运行 Let the method you're calling decide whether to make a worker thread or not. 让你正在调用的方法决定是否创建一个工作线程。 Async methods are already asynchronous . 异步方法已经是异步的 You don't need to do anything to them to make them asynchronous. 您无需对它们执行任何操作即可使它们异步。 Await does not make anything asynchronous. Await不会使任何异步。

Await exists solely to make it easier for the developer to check whether an asynchronous operation has completed, and to sign up the remainder of the current method as the continuation if it has not completed. Await的存在仅仅是为了让开发人员更容易检查异步操作是否已经完成,并且如果尚未完成,则将当前方法的剩余部分注册为继续。 That's what it is for. 这就是它的用途。 Again, await does not create asynchrony . 同样, await不会创建异步 Await helps you build asynchronous workflows . Await 可帮助您构建异步工作流 An await is a point in the workflow where an asynchronous task must be completed before the workflow can continue . 等待是工作流中的一个点,在工作流可以继续之前必须完成异步任务

I also know that we use Task.Run() to execute CPU bound code to look for an available thread in thread pool. 我也知道我们使用Task.Run()来执行CPU绑定代码以在线程池中查找可用线程。 Is this true ? 这是真的 ?

That's correct. 那是对的。 If you have a synchronous method, and you know that it is CPU bound, and you would like it to be asynchronous, and you know that the method is safe to run on another thread, then Task.Run will find a worker thread, schedule the delegate to be executed on the worker thread, and give you a task representing the asynchronous operation. 如果您有一个同步方法,并且您知道它是CPU绑定的,并且您希望它是异步的,并且您知道该方法在另一个线程上运行是安全的,那么Task.Run将找到一个工作线程,时间表要在工作线程上执行的委托,并为您提供表示异步操作的任务。 You should only do this with methods that are (1) very long-running, like, more than 30 milliseconds, (2) CPU bound, (3) safe to call on another thread. 您应该只对以下方法执行此操作:(1)超长运行,超过30毫秒,(2)CPU绑定,(3)在另一个线程上安全调用。

If you violate any of those, bad things happen. 如果你违反任何一个,就会发生坏事。 If you hire a worker to do less than 30 milliseconds of work, well, think about real life. 如果你雇佣一名工人做不到30毫秒的工作,那就考虑一下现实生活。 If you have some computations to do, does it make sense to buy an ad, interview candidates, hire someone, get them to add three dozen numbers together, and then fire them? 如果您有一些计算要做,那么购买广告,面试候选人,雇用某人,让他们一起添加三打数字然后解雇它们是否有意义? Hiring a worker thread is expensive . 雇用工人线程是昂贵的 If hiring the thread is more expensive than just doing the work yourself, you will not get any performance win at all by hiring a thread; 如果雇佣线程比仅仅自己完成工作更昂贵,那么通过雇佣一个线程,你将无法获得任何绩效奖励; you'll make it a lot worse. 你会让事情变得更糟。

If you hire a worker to do IO bound tasks, what you've done is hired a worker to sit by the mailbox for years and yell when mail arrives. 如果你雇佣一名工人来完成IO约束任务,你所做的就是雇佣一名工作人员在邮箱旁坐多年,并在邮件到达时大喊大叫。 That does not make the mail arrive faster . 这不会使邮件更快到达 It just wastes worker resources that could be spent on other problems. 它只会浪费可用于其他问题的工人资源。

And if you hire a worker to do a task that is not threadsafe, well, if you hire two workers and tell them to both drive the same car to two different locations at the same time, they're going to crash the car while they're fighting over the steering wheel on the freeway. 如果你雇佣一名工人来完成一个非线程安全的任务,那么,如果你雇用两名工人并告诉他们同时将同一辆车开到两个不同的地方,那么他们就会让他们撞车。在高速公路上的方向盘上作战。

If it's like that there's no time gain when using await async since no extra thread is used. 如果它是这样的,那么在使用等待异步时没有时间增益,因为没有使用额外的线程。

This is correct. 这是对的。 By themselves, async and await do not directly use threads. 就其本身而言, asyncawait不直接使用线程。 Their purpose is to free up the calling thread . 他们的目的是释放调用线程

What i knew so far is that only Task.Run() create a new thread. 到目前为止我所知道的是只有Task.Run()创建一个新线程。 Is this also true for Task.WhenAll() or Task.WhenAny() ? 对于Task.WhenAll()或Task.WhenAny(),这也是如此吗?

No; 没有; neither Task.WhenAll nor Task.WhenAny directly use any threads. Task.WhenAllTask.WhenAny直接使用任何线程。

When creating the getStringTask Task, another thread will copy the current context and start executing the GetStringAsync method. 创建getStringTask任务时,另一个线程将复制当前上下文并开始执行GetStringAsync方法。

No. GetStringAsync is invoked synchronously on the current thread, just like any other method. 不会。在当前线程上同步调用GetStringAsync ,就像任何其他方法一样。 It returns an incomplete task, again, synchronously. 它同步返回一个不完整的任务。

When awaiting getStringTask, we will see if the other thread has completed his task, if not the control will be back the caller of AccessTheWebAsync() method until the other thread complets it's task to resume the control. 在等待getStringTask时,我们将看到其他线程是否已完成其任务,如果不是,则控件将返回AccessTheWebAsync()方法的调用者,直到另一个线程完成其恢复控制的任务为止。

Close, except that there's no other thread. 关闭,除了没有其他线程。 await getStringTask will check to see if the task is completed; await getStringTask将检查任务是否完成; if it's not, then it will return an incomplete task from AccessTheWebAsync . 如果不是,那么它将从AccessTheWebAsync返回一个不完整的任务。

Can someone exmplain what exactly happening when awaiting a Task ? 有人可以在等待任务时解除究竟发生了什么吗?

I recommend reading my async intro for more details. 我建议阅读我的async介绍以获取更多详细信息。

Your basic assumption — a Task always runs on a thread — is indeed wrong. 你的基本假设 - 一个Task总是在一个线程上运行 - 确实是错误的。 A simple counterexample would be a timer-based task which doesn't run at all: it just subscribes on timer and sets task state to completed whenever the timer fires. 一个简单的反例是基于计时器的任务,它根本不运行:它只是订阅计时器,并在计时器触发时将任务状态设置为完成。

More useful and more practical example of the tasks not running anywhere — network requests: they send the request, subscribe to the incoming answer and just stop running, freeing thread for another work*. 更有用,更实用的任务无法运行的例子 - 网络请求:他们发送请求,订阅传入的答案,然后停止运行,释放线程以进行其他工作*。

So let's consider your actual questions. 那么让我们考虑你的实际问题。


What i knew so far is that only Task.Run() create a new thread. 到目前为止我所知道的是只有Task.Run()创建一个新线程。 Is this also true for Task.WhenAll() or Task.WhenAny() ? 对于Task.WhenAll()或Task.WhenAny(),这也是如此吗?

No, Task.WhenAll won't create any new threads. 不, Task.WhenAll不会创建任何新线程。 It will wait for the already existing tasks to complete regardless of where they run (and regardless of if they run in any thread at all!). 它将等待已经存在的任务完成,无论它们在何处运行(无论它们是否在任何线程中运行!)。

The task created by Task.WhenAll is not running in any specific thread itself! Task.WhenAll创建的任务不在任何特定的线程本身运行! It just detects when the underlying tasks are finished, and after all of them are ready, finishes itself too. 它只是检测底层任务何时完成,并且在所有这些任务准备好之后,也完成了自己。 Task.WhenAll doesn't need any thread for doing this. Task.WhenAll不需要任何线程来执行此操作。


When creating the getStringTask Task, another thread will copy the current context and start executing the GetStringAsync method. 创建getStringTask任务时,另一个线程将复制当前上下文并开始执行GetStringAsync方法。

Calling an async method like GetStringAsync , as we seen before, won't be executed on any specific thread. 如前所述,调用像GetStringAsync这样的异步方法不会在任何特定线程上执行。 The code of GetStringAsync sets up the things so that it gets control back (perhaps on a thread pool thread) when the answer comes, and yields control back to you. GetStringAsync的代码设置了这些东西,以便在答案到来时它可以获得控制权(可能在线程池线程上),并将控制权交还给您。 The preparation work can be perfectly done on current thread, it doesn't take too much time*. 准备工作可以在当前线程上完美完成,不需要太多时间*。


*Disclaimer: it's a simplification, indeed the sequence of actions done by a network async request is much more complicated. *免责声明:这是一种简化,实际上网络异步请求所做的一系列操作要复杂得多。

An article that helped me a lot to understand async-await is this interview with Eric Lippert , where he compares async-await with a cook making breakfast. 一篇帮助我理解async-await的文章是对Eric Lippert的采访 ,在那里他将async-await与做早餐的厨师进行了比较。 Search somewhere in the middle for async-await. 在中间某处搜索async-await。

If a cook has to make breakfast and he just put some bread in the toaster, he doesn't wait idly for the bread to be toasted, but starts looking around to see if he can do something else, for instance boiling water for the tea. 如果一个厨师必须做早餐,他只是在烤面包机里放一些面包,他不会懒得等待烤面包,而是开始环顾四周,看看他是否可以做其他事情,比如用茶来煮水。

Something similar happens when you see async-await. 当你看到async-await时会发生类似的事情。 If you call an async function, you know that somewhere inside is an await. 如果你调用异步函数,你知道里面的某个地方是等待的。 In fact, your compiler will warn you if you forget to await in your async function. 实际上,如果忘记等待异步函数,编译器会发出警告。

Once your thread sees the await, it does not wait idly for the awaitable task to finish, but it looks around to see if it can do other things. 一旦你的线程看到await,它就不会等待等待等待的任务完成,但它会四处查看是否可以执行其他操作。 It can go up the call stack to see if one of the callers is not awaiting yet and execute those statements until it sees an await. 它可以向上调用堆栈以查看其中一个调用者是否还在等待并执行这些语句,直到它看到等待。 Go up the call stack again and execute statements until you see an await. 再次上调调用堆栈并执行语句,直到看到等待。

There is no guarantee that the thread that continues the statements after your not-awaited async call is the same as your original thread. 无法保证在您未等待的异步调用之后继续语句的线程与原始线程相同。 But because this thread has the same "context" you can act as if it is the same thread. 但是因为这个线程具有相同的“上下文”,所以你可以表现得好像它是同一个线程。 There is no need for a critical section and the like. 不需要临界区等。

Console.Writeline(Thread.CurrentThread.ManagedThreadId);

// async call to the text reader to read a line; don't await
var taskReadLine = myTextReader.ReadLineAsync()

// because I did not await, the following will be executed as soon as a thread is free
Console.Writeline(Thread.CurrentThread.ManagedThreadId);
...

// we need the read line; await for it
string readLine = await taskReadLine;
Console.Writeline(Thread.CurrentThread.ManagedThreadId);
ProcessReadLine(readLine);

There is no guarantee that the thread that does the DoSomething is the same thread that was used to call the ReadLineAsync. 无法保证执行DoSomething的线程与用于调用ReadLineAsync的线程相同。 If you execute the code in a simple test program, chances are high that you get more than one thread id. 如果您在一个简单的测试程序中执行代码,那么您获得多个线程ID的可能性很高。

Your code should not depend on any statement within the async function to be executed before you await the result: 在等待结果之前,您的代码不应该依赖于要执行的异步函数中的任何语句:

async Task<int> DoIt()
{
    this.X = 4;
    await DoSomethingElseAsync(this.X);
    return 5;
}
async Task CallDoItAsync()
{
    this.X = 0;
    var taskDoIt = DoIt();

    // you didn't await, it is not guaranteed that this.X already changed to 4
    ...
    int i = await taskDoIt();
    // now you can be certain that at some moment 4 had been assigned to this.X 

Creating a Task object does not create a thread. 创建Task对象不会创建线程。 Creating a thread is fairly expensive. 创建一个线程相当昂贵。 Therefore your process has a thread pool, containing several threads. 因此,您的进程有一个包含多个线程的线程池。 Threads that are idle are put in the pool and are available to do other things upon request. 空闲的线程放在池中,可根据请求执行其他操作。 As soon as your process needs a thread it takes an available thread from the thread pool and schedules it for running. 只要您的进程需要一个线程,它就会从线程池中获取一个可用的线程并安排它运行。

I'm not sure what happens if no thread if available in the pool. 我不确定如果池中没有可用的线程会发生什么。 I guess that your function simply has to wait for an available thread. 我想你的函数只需要等待一个可用的线程。

You can access the thread pool using the static ThreadPool class. 您可以使用静态ThreadPool类访问线程池。

ThreadPool.GetMaxThreads (out int workerThreads, out int completionPortThreads);
++workerThreads;
++completionPortThreads;
bool success = ThreadPool.SetMaxThreads (workerThreads, completionPortThreads);

Be very careful changing the thread pool! 更改线程池要非常小心!

Some people say that async-await is only useful to keep the UI responsive, but the following shows that it also can improve processing speed. 有人说async-await仅对保持UI响应有用,但以下表明它也可以提高处理速度。

Non-async: 非异步:

void CopyFile(FileInfo infile, FileInfo outFile)
{
     using(var textReader = inFile.OpenText())
     {
        using (var textWriter = outFile.CreateText())
        {
            // Read a line. Wait until line read
            var line = textReader.ReadLine();
            while (line != null)
            {
                // Write the line. Wait until line written
                textWrite.WriteLine(line);

                // Read the next line. Wait until line read
                line = textReader.ReadLine();
            }
        }
    }
}

You see all the waits. 你看到了所有的等待。 Luckily a TextReader and TextWriter do buffer data, otherwise we really had to wait until the data was written before the next line was read 幸运的是,TextReader和TextWriter会缓冲数据,否则我们必须等到数据写入才读取下一行

async Task CopyFileAsync(FileInfo infile, FileInfo outFile)
{
     using(var textReader = inFile.OpenText())
     {
        using (var textWriter = outFile.CreateText())
        {
            // Read a line. Wait until line read
            var line = await textReader.ReadLineAsync();
            while (line != null)
            {
                // Write the line. Don't wait until line written
                var writeTask = textWrite.WriteLineAsync(line);

                // While the line is being written, I'm free to read the next line. 
                line = textReader.ReadLine();

                // await until the previous line has been written:
                await writeTask;
            }
        }
    }
}

While a line is being written, we already try to read the next line. 在写一行时,我们已经尝试阅读下一行。 This can improve processing speed. 这可以提高处理速度。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM