跨越异步边界的布尔标志的线程安全

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,或者使用CancellationTokenCancellationTokenSource 你可能会?应该?甚至必须? 我认为这没有任何问题。基于此,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 标志将按您的预期工作,因为在设置它的方法是 awaited 之后检查它。在await 之后运行的代码将看到由 awaited 方法引起的所有副作用。

但是,timeout 看起来根本不是线程安全的。等待的代码检查timeout,但(可能)不是awaiting 任何代码设置 timeout

您可能会遇到使用锁之类的麻烦,或者您可以使用 .NET 框架提供的 CancellationTokenSourceCancellationTokenOperationCanceledException 来解决这种情况:

try

  await MyAsyncJob.Process(() =>
  
    cancellationToken.ThrowIfCancellationRequested();
  );

catch (OperationCanceledException)

  DoSomething();

通过关注standard pattern,其他开发人员将能够更快地理解您的代码。而且您根本不必担心线程安全;提供的框架类型会处理它。

【讨论】:

你不能在等待块之后检查cancellationToken.IsCancellationRequested,避免抛出吗?这当然假设在超时时在等待块内请求取消。 @BenjaminE。标准模式是在取消操作时引发OperationCanceledException 异常。您可以选择检查IsCancellationRequested,但如果您将CancellationToken 传递给任何其他 方法,那么如果取消(遵循标准模式)它们将响应异常,因此您通常会结束无论如何都需要异常处理。

以上是关于跨越异步边界的布尔标志的线程安全的主要内容,如果未能解决你的问题,请参考以下文章

如果我只是想以线程安全的方式测试和设置标志,那么线程类是什么?

有啥可以使静态布尔线程安全的吗?

C ++如果一个线程写入一旦完成就会切换一个布尔值,那么在另一个线程的循环中读取该布尔值是不是安全?

对线程安全, 可重入函数, 异步安全的理解

异步代码、共享变量、线程池线程和线程安全

C#/.NET 中不可重置的“标志”线程是不是安全?