很长一段时间以来,我一直试图理解.NET中的异步等待内容,但我努力取得成功,所以在使用异步时总会发生完全出乎意料的事情。
这是我的应用程序:
namespace ConsoleApp3
{
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Hello World!");
var work1 = new WorkClass();
var work2 = new WorkClass();
while(true)
{
work1.DoWork(500);
work2.DoWork(1500);
}
}
}
public class WorkClass
{
public async Task DoWork(int delayMs)
{
var x = 1;
await Task.Delay(delayMs)
var y = 2;
}
}
}
这只是我创建的一个示例,用于检查代码将如何执行。有几件事使我感到惊讶。
首先,涉及很多线程!如果我在var y = 2;
上设置了一个断点,我会发现那里的threadId不相同,它可以是1、5、6或其他。
这是为什么?我认为async / await不会自己使用其他线程,除非我明确命令(使用Task.Run或创建新线程)。至少this article试图说出我的想法。
好吧,但是我们可以说出于某种原因还有其他一些线程-即使它们是,我的await Task.Delay(msDelay);
也没有ConfigureAwait(false)
!据我了解,没有此调用,线程就不会改变。
我很难很好地理解这个概念,因为我找不到任何可以包含所有信息而不仅仅是几条信息的优质资源。
答案 0 :(得分:9)
当异步方法等待某件事时,如果它还没有完成,它将安排一个继续,然后返回。问题是继续在哪个线程上运行。如果存在同步上下文,则将继续运行安排在该上下文中运行-通常是UI线程,或者可能是特定的线程池。
在您的情况下,您正在运行一个控制台应用程序,这意味着 没有同步上下文(SynchronizationContext.Current
将返回null)。在这种情况下,继续在线程池线程上运行。并不是专门创建一个新的线程来运行延续-而是线程池将选择延续,而“主”线程将不运行延续。
ConfigureAwait(false)
用来表示您不想返回到当前的同步上下文进行延续-但是由于您的情况下没有同步上下文,因此没有任何区别。
答案 1 :(得分:1)
Async / await不会自己使用其他线程,但是在您的示例中,它不是自己使用。您正在调用Task.Delay
,并且此方法计划在线程池线程中运行的继续。在延迟期间,没有线程被阻塞。不会创建新线程。时间到来时,将使用现有线程来运行延续,在您的情况下,它几乎不需要做任何工作(只需运行var y = 2
分配),因为您甚至都没有等待{{1}返回的任务}。完成这项工作(几分之一秒后)后,线程池线程再次可以执行其他工作。
您可以DoWork
而不使用Task.Delay
,而该方法根本不使用线程,或者创建专用的长时间运行的线程的方法,也可以启动新进程的方法。异步/等待对这些都不负责。异步/等待只是一种以开发人员友好的方式创建任务延续的机制。
在这里,您的应用程序已针对没有异步/等待的世界进行了修改:
await