可以从多个线程等待相同的任务 - 等待线程安全吗?
Posted
技术标签:
【中文标题】可以从多个线程等待相同的任务 - 等待线程安全吗?【英文标题】:Is it ok to await the same task from multiple threads - is await thread safe? 【发布时间】:2013-05-25 02:19:47 【问题描述】:等待线程安全吗?似乎 Task 类是线程安全的,所以我想等待它也是线程安全的,但我没有在任何地方找到确认。线程安全也是自定义等待器的要求 - 我的意思是 IsCompleted、GetAwaiter 等方法? IE。如果这些方法不是线程安全的,那么等待是线程安全的吗?但是,我预计不会很快需要自定义等待器。
用户场景示例:假设我有一个异步返回结果的后台任务,然后从多个线程中使用:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace scratch1
class Foo
Task<int> _task;
public Foo()
_task = Task.Run(async () => await Task.Delay(5000); return 5; );
// Called from multiple threads
public async Task<int> Bar(int i)
return (await _task) + i;
class Program
public static void Main(string[] args)
Foo foo = new Foo();
List<Task> tasks = new List<Task>();
foreach (var i in Enumerable.Range(0, 100))
tasks.Add(Task.Run(async () => Console.WriteLine(await foo.Bar(i)); ));
Task.WaitAll(tasks.ToArray());
【问题讨论】:
【参考方案1】:正如你所提到的,await
足够薄,一开始就看不到线。
与await
相关的代码(状态机中编译器生成的代码,以及BCL 中的支持类)一次只能在一个线程上运行。 (从等待返回时可以切换到不同的线程,但之前的运行已经完成)
实际的线程安全取决于您等待的对象。
它必须有一种线程安全的方式来添加回调(或者await
一次两次可能会中断)。
此外,它必须只调用一次回调,否则可能会破坏状态机。 (特别是,如果它同时在两个线程上运行它的回调,事情会变得非常糟糕)
Task
是线程安全的。
【讨论】:
您的第一个陈述可能会误导人们认为await
从根本上来说是不安全的......而我认为您的意思是它既不是本质上的安全也不是不安全的——它只是代表正在等待的任何东西,如果那是@ 987654326@,你没事。
当然,谢谢。虽然“将只在一个线程上运行”可能应该是“每次只在一个线程上运行,每次方法调用(对应于状态机的单个实例)”。它当然可以在await
点的线程之间跳来跳去,但它不会在多个线程上同时运行。 (至少不会,除非你严重滥用它......这总是很有趣。)以上是关于可以从多个线程等待相同的任务 - 等待线程安全吗?的主要内容,如果未能解决你的问题,请参考以下文章