【问题标题】:Do async and await produce acquire and release semantics?async 和 await 会产生获取和释放语义吗?
【发布时间】:2019-03-13 09:43:40
【问题描述】:

关于从async 方法返回是否总是产生释放语义以及await 是否总是产生获取语义,我找不到明确的答案。我认为是的,否则任何async/await 代码都会是雷区?

下面是一个例子:返回值是否都保证为100*212345*2,没有任何明确的锁或障碍?

private static async Task<(int, int)> AMethod()
{
    // Runs on the original thread:
    var x = 100;
    var y = 12345;

    var task = Task.Run(() =>
    {
        // Can run on another thread:
        x *= 2;
        y *= 2;

        // Implicit return here, marking the task completed.
        // Release semantics or not?
    });

    await task; // Acquire semantics or not?

    // Runs on the original thread:
    return (x, y);
}

编辑:当然,Task.Run 还需要在开始运行任务代码时生成发布和获取。忘记了原始问题中的那些。

【问题讨论】:

  • 添加await task.ConfigureAwait(false) 。然后你绝对不能保证 x 和 y 将是 100*212345*2
  • @YauhenSampir 不,这对问题没有任何影响。
  • @YauhenSampir 如果发布和获取都在那里,那么无论.ConfigureAwait(false) 如何,您仍然会得到一个不错的结果。如果没有,它已经被破坏了。

标签: c# .net async-await language-lawyer memory-model


【解决方案1】:

是的,返回的值都保证为100*212345*2,没有任何显式的锁或屏障。

在这种情况下产生内存屏障的是Task.Run,而不是await

引用wonderful Albahari Threading in C#

以下隐式生成完整的栅栏:

  • C# 的锁定语句 (Monitor.Enter/Monitor.Exit)
  • Interlocked 类的所有方法(我们很快就会介绍)
  • 使用线程池的异步回调——包括异步委托、APM 回调和任务延续
  • 设置和等待信号结构
  • 任何依赖信号的事情,例如开始或等待任务

由于最后一点,以下是线程安全的:

int x = 0;
Task t = Task.Factory.StartNew (() => x++);
t.Wait();
Console.WriteLine (x);    // 1

Task.Run 包装 ThreadPool.UnsafeQueueUserWorkItem,属于“使用线程池的异步回调”。

请参阅Memory barrier generators,了解更全面的造成内存障碍的事物列表。

【讨论】:

  • 当然,开始任务也需要获取和释放,而不仅仅是完成。呃。谢谢你提到这一点。将编辑问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-08-21
  • 1970-01-01
  • 2014-03-13
  • 2011-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多