【发布时间】:2018-11-16 16:28:30
【问题描述】:
考虑以下修改非线程安全列表的异步函数:
async Task AddNewToList(List<Item> list)
{
// Suppose load takes a few seconds
Item item = await LoadNextItem();
list.Add(item);
}
简单地说:这安全吗?
我担心有人可能会调用异步方法,然后在加载时(在另一个线程上或作为 I/O 操作),调用者可能会修改列表。
假设调用者正在执行 list.Clear(),例如,突然 Load 方法结束!会发生什么?
任务会立即中断并运行list.Add(item); 代码吗?或者它会等到主线程完成所有预定的 CPU 任务(即:等待 Clear() 完成),然后再运行代码?
编辑:因为我基本上已经在下面为自己回答了这个问题,所以这里有一个额外的问题:为什么?为什么它会立即中断而不是等待 CPU 绑定操作完成?不排队似乎是违反直觉的,这将是完全安全的。
编辑:这是我自己测试的另一个示例。 cmets 指示执行顺序。我很失望!
TaskCompletionSource<bool> source;
private async void buttonPrime_click(object sender, EventArgs e)
{
source = new TaskCompletionSource<bool>(); // 1
await source.Task; // 2
source = null; // 4
}
private void buttonEnd_click(object sender, EventArgs e)
{
source.SetResult(true); // 3
MessageBox.Show(source.ToString()); // 5 and exception is thrown
}
【问题讨论】:
-
答案取决于您的SynchronizationContext,对于不同的应用程序不同。例如,您在 WinForms 和 ASP.NET 中可能是安全的,但在 Console 应用程序或 .NET Core 应用程序中并不安全,因为它们不需要连续运行来连续运行。请编辑您的问题并标记平台。此外,如果您使用
async void,所有赌注都将取消。 -
@JohnWu 我用我想出的一个例子来编辑我的问题来为自己测试。这在 WinForms 中绝对不安全。
-
这实际上更多地展示了 TaskCompletionSource 的一个陷阱。问题是,默认情况下,SetX 方法(SetResult、SetException 等)会导致延续同步运行,而这里
source = null是延续。您必须特别注意如何使用 TaskCompletionSource。 -
@mikez 你能举个例子说明我不会遇到这个问题吗?我错误地认为无论任务如何它都是一致的,但你的解释是有道理的。
-
只要您的任务/线程以不安全的方式处理共享状态,您就会遇到问题。就这么简单,但这也应该为您提供有关您需要做什么的线索,只是不要使用共享状态,或者至少当您这样做时,请确保您知道该共享状态发生了什么并防止出现问题。
标签: c# asynchronous thread-safety