【问题标题】:Race Condition in Async/Await CodeAsync/Await 代码中的竞争条件
【发布时间】:2015-03-03 19:15:04
【问题描述】:

我只是想知道下面的代码中是否出现了竞态条件:

int readingFiles;
async Task<string> ReadFile (string file)
{    
    ++readingFiles;

    var text = await Stream.ReadFileAsync(file);

    --readingFiles;

    return text;
}

如果 ReadFile 方法由线程池线程执行,则 readingFiles 将由两个不同的线程访问,并且 readingFiles 变量不受任何同步习惯用法的保护。

这意味着对正在执行“--readingFiles”的其他线程不应该看到对 readingFiles 的第一次更新。但是,我从来没有见过在“--readingFiles”之后readingFiles 等于-1。我使用 Thread.CurrentThread 检查同一个线程是否执行 ++ 和 -- 操作。在大多数情况下,它不是同一个线程,我仍然没有看到 readingFiles 为 -1。

即使存在竞争条件并且 readingFiles 不是易失性的,为什么我看不到这种竞争条件的效果?

【问题讨论】:

  • 当然,async/await 使用当前同步上下文的全部方式意味着 适当的“同步习语”到位 - 你只是看不到他们。
  • 代替--readingFiles试试这个:var temp = readingFiles; Sleep(1000); readingFiles = temp - 1;
  • 你只运行了一个 ReadFile 实例,对吧?

标签: c# multithreading asynchronous async-await race-condition


【解决方案1】:

这里没有竞争条件。 .NET 运行时将插入适当的内存屏障。

另见 cmets:http://blogs.msdn.com/b/pfxteam/archive/2012/04/12/async-await-faq.aspx

是的,TPL 在任务排队时和任务执行的开始/结束时包含适当的障碍,以便适当地使值可见。

【讨论】:

  • 没有竞争条件当你调用这个方法一次。如果您并行调用它多次(在同一个实例上),那当然是不安全的。只是把它扔在那里。
  • 所以 await 语句是一种内存屏障。
  • @TomK.:是的;它会为您解决记忆障碍。正如 Servy 所提到的,您当然必须小心不要同时从多个线程访问共享数据。
【解决方案2】:

这里可能会发生很多事情。

首先,您正在运行哪种可执行文件?当 Await 触发时,它使用当前同步上下文,因此您等待的代码可能会被序列化为 1 个 UI 线程。

此外,由于变量周围没有内存屏障/波动性保护,您的线程可能正在读取单独缓存的值(正如@Spo1ler 在他的帖子中提到的那样)

此外,线程池可能会选择在同一个线程上运行您的两个请求(这样做是它的权利 - 您让 .net/windows 决定何时以及如何分配线程)

不过,归根结底,您确实应该通过同步或互锁操作来保护对变量的访问。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-16
    • 1970-01-01
    相关资源
    最近更新 更多