当一个线程“等待”某个东西时会发生什么?

4

当一个async方法await一个Task时,它当前运行的线程会发生什么?

我猜测在UI线程上,消息循环会恢复,在线程池线程上,线程会释放回线程池。但如果线程是手动启动的,会发生什么?还有其他类型的线程吗?

3个回答

3
这花了我很长时间才意识到,但async-await的这部分非常简单,只需沿着调用堆栈向上走。每当一个方法awaits(假设它不是已完成的Task或类似内容)时,它就会返回给调用者。由于我们正在讨论代码放弃对线程控制的点,这意味着这是堆栈顶部的最后一块代码。
如果我们在UI线程上运行,我们将返回到消息循环。如果我们在线程池线程上,控制权将返回到线程池。手动创建的线程仅运行void方法,只有当它是async void方法时,您才能使用await,这意味着即使在方法完成之前,它也将在第一个await处结束线程。
延续工作方式相同。它将在UI线程或线程池上排队,然后再次awaits或完成,再次放弃控制权。
编辑:我进行了一些自定义任务调度器的实验,您可以应用完全相同的逻辑。当任务等待时,它放弃控制。我使用的任务调度器基于此单线程任务调度器。在这种情况下,放弃控制意味着任务调度器开始处理队列中的下一个任务。还要注意,除非使用ConfigureAwait(false),否则将使用当前任务调度程序安排延续。

1
这是一个不完整且部分不正确的答案。手动线程、线程池线程或任何线程都可以运行我们想要的任何异步方法,只需要将其包装成任务(Task),然后在该线程上同步运行即可。 - ipavlu
@ipvalu 问题是当一个线程异步等待某些东西时会发生什么。同步等待任务是完全不同的事情。 - BetaKeja
你说手动创建的线程只能运行异步 void 方法。没有任何同步上下文、调度器的线程可以运行任何东西,甚至是异步任务方法。当你调用任何异步方法时,它就像 Task.RunSynchronously,它会同步执行包括其中的等待,只要它们是已完成的等待,并在所有同步部分完成或第一个未完成的等待时返回(然后其余部分通常在线程池上运行)。 - ipavlu
你的线程中没有等待。你不能在异步方法之外使用await,因为你的线程没有同步上下文、调度程序或消息泵,所以你无法执行必要的操作来使它等待 => 运行其继续(在异步方法中未完成的await调用后面的代码,稍后将被完成)。 - ipavlu

0
请注意,没有所谓的手动线程,它们都只是线程。就像电子一样,没有两种类型的电子:)。
ThreadPool 线程、你的线程、UI 线程、COM 线程等之间的区别在于线程是否具有消息循环、同步上下文。
如果线程具有同步上下文,则我们启动的所有内容都不会直接运行,而是从某个队列中作为委托启动,并且当队列为空时,线程处于等待状态。
因此,如果代码的一部分在异步方法中正在运行并且在具有同步上下文的线程上运行,则在等待时它会存储上下文(除非使用了ConfigureAwait),该方法已经完成并正在等待下一个委托或消息以进行执行。因此,当 await 完成而未使用 ConfigureAwait 将下一段代码移至线程池时,它将发送新的委托到同步上下文,即 await 后面的代码将被处理。

-1

实际上,任何线程都可以同步运行异步方法,并且它将等待直到

=>

Stephen Cleary指出,这部分内容不正确:“等待异步方法完成”。措辞真的非常误导人。因此,也许这会更好一点。

=>

所有同步执行的部分都已完成。可以等待已完成的任务,例如Task.FromResult。此外,它可能是另一个异步方法,可以完全同步地执行。

但是,在某个时刻,等待某个操作的位置无法在提供awaiter之前完成,则t.RunSynchronously将退出,即使异步方法尚未完全运行。

void ThreadWorker(object obj)
{
    var t = new Task<Task>(AsyncMethod);
    t.RunSynchronously();
    //in this case, it runs the AsyncMethod completely
    //and effectively waits for result
}

async Task AsyncMethod()
{
   await Something().ConfigureAwait(false);
   //everything from here runs on the ThreadPool
}

这个答案是错误的。RunSynchronously 会等待任务 t 完成;它不会等待从 AsyncMethod 返回的任务完成。 - Stephen Cleary
嗯,有时候是这样,但并非总是如此,这取决于情况。我认为,如果我更准确地说,它会等待异步方法中所有同步处理的等待操作完成,包括等待已完成的任务,但通常不会等待异步方法的完成。 - ipavlu
等待部分的措辞不正确,我只想指出线程不仅可以运行异步void方法,而且可以使用t.RunSynchronously异步任务方法调用。 - ipavlu
哦,我意识到将包装和同步运行与直接调用AsyncMethod();在ThreadWorker方法中做的事情是一样的,除非显式需要调度程序 :) ... - ipavlu
也不是点赞的答案:“即使在方法完成之前,它也会在第一个await处结束线程。”从技术上讲并不是不正确的,但是它是不完整的。我的回答正是指出了这一点... - ipavlu
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接