跨越异步边界的布尔标志的线程安全
Posted
技术标签:
【中文标题】跨越异步边界的布尔标志的线程安全【英文标题】:Thread-safety of bool flag crossing an async boundary 【发布时间】:2020-02-05 07:42:44 【问题描述】:是否可以假定以下情况实际上是线程安全的? bool
标志总是从 false 开始,并且只有一种方式变为 true 或保持原样。
我知道它实际上不是线程安全的,但是真的有可能以某种方式中断吗?
在这种情况下,异步执行甚至是严格排序的,从不并行。
bool cancelled = false;
await MyAsyncJob.Process(() =>
if (timeout)
cancelled = true;
);
if (cancelled)
DoSomething();
【问题讨论】:
您可能希望将布尔值标记为volatile
,或者使用CancellationToken
和CancellationTokenSource
。
你可能会?应该?甚至必须?
我认为这没有任何问题。基于此,lambda 已完全执行。
顺便说一句,"I discourage you from ever making a volatile field. Volatile fields are a sign that you are doing something downright crazy" - Eric Lippet。奥哈德的重点。最初来自归档的 MSDN 博客blogs.msdn.com/b/ericlippert/archive/2011/06/16/…
Stephen Cleary 对这个问题做了一些有见地的 cmets:没有记录,但微软人员向我保证,如果 await 导致线程切换,障碍总是存在的。 ¹ await 注入了所有必要的线程屏障,因此不存在乱序读取或类似问题。 ²
【参考方案1】:
是否可以假定以下情况实际上是线程安全的?布尔标志...
不清楚你指的是哪个标志。
在您的代码中,cancelled
标志将按您的预期工作,因为在设置它的方法是 await
ed 之后检查它。在await
之后运行的代码将看到由 awaited 方法引起的所有副作用。
但是,timeout
看起来根本不是线程安全的。等待的代码检查timeout
,但(可能)不是await
ing 任何代码设置 timeout
。
您可能会遇到使用锁之类的麻烦,或者您可以使用 .NET 框架提供的 CancellationTokenSource
、CancellationToken
和 OperationCanceledException
来解决这种情况:
try
await MyAsyncJob.Process(() =>
cancellationToken.ThrowIfCancellationRequested();
);
catch (OperationCanceledException)
DoSomething();
通过关注standard pattern,其他开发人员将能够更快地理解您的代码。而且您根本不必担心线程安全;提供的框架类型会处理它。
【讨论】:
你不能在等待块之后检查cancellationToken.IsCancellationRequested
,避免抛出吗?这当然假设在超时时在等待块内请求取消。
@BenjaminE。标准模式是在取消操作时引发OperationCanceledException
异常。您可以选择检查IsCancellationRequested
,但如果您将CancellationToken
传递给任何其他 方法,那么如果取消(遵循标准模式)它们将响应异常,因此您通常会结束无论如何都需要异常处理。以上是关于跨越异步边界的布尔标志的线程安全的主要内容,如果未能解决你的问题,请参考以下文章
如果我只是想以线程安全的方式测试和设置标志,那么线程类是什么?