【问题标题】:Is there a race condition in the `latch` sample in N3600?N3600 中的“闩锁”示例中是否存在竞争条件?
【发布时间】:2013-04-26 16:51:11
【问题描述】:

建议包含在 C++14(又名 C++1y)中的是一些新的线程同步原语:锁存器和屏障。建议是

这听起来是个好主意,示例使它看起来对程序员非常友好。不幸的是,我认为示例代码调用了未定义的行为。该提案提到latch::~latch()

破坏闩锁。如果闩锁在其他线程处于wait() 或正在调用count_down() 时被销毁,则行为未定义。

请注意,它说的是“在wait()”中,而不是“在wait() 中被阻止”,正如count_down() 的描述所使用的那样。

那么提供以下示例:

第二个用例的示例如下所示。我们需要加载数据,然后使用多个线程对其进行处理。加载数据受 I/O 限制,而启动线程和创建数据结构受 CPU 限制。通过并行运行这些,可以增加吞吐量。

void DoWork()
{
    latch start_latch(1);
    vector<thread*> workers;
    for (int i = 0; i < NTHREADS; ++i) {
      workers.push_back(new thread([&] {
        // Initialize data structures. This is CPU bound.
        ...
        start_latch.wait();
        // perform work
        ...
      }));
    }
    // Load input data. This is I/O bound.
    ...
    // Threads can now start processing
    start_latch.count_down();
}

wait() 唤醒和返回的线程与离开作用域时锁存器的破坏之间是否存在竞争条件?除此之外,所有thread 对象都被泄露。如果调度程序在count_down 返回并且start_latch 对象离开范围之前没有运行所有工作线程,那么我认为会导致未定义的行为。大概解决方法是在count_down 之后但在返回之前迭代向量和join()delete 所有工作线程。

  1. 示例代码有问题吗?
  2. 您是否同意提案应显示完整正确的示例,即使任务非常简单,以便审阅者了解使用体验?

注意:似乎有一个或多个工作线程尚未开始等待,因此将在已损坏的闩锁上调用 wait()


更新:现在有一个新版本的提案,但代表性示例没有改变。

【问题讨论】:

  • @stefan:我不认为“释放阻塞线程”包括“至少等待那些线程运行直到从wait()返回”
  • 此外,其中一个线程可能还没有*到达`start_latch.wait() 调用。
  • 我同意你的看法。好像例子坏了……

标签: c++ race-condition language-lawyer c++14


【解决方案1】:

感谢您指出这一点。是的,我认为示例代码(在其辩护中旨在简洁)被破坏了。它可能应该等待线程完成。

任何允许线程在 wait() 中被阻塞的实现几乎肯定会涉及某种条件变量,并且在线程尚未退出 wait() 时销毁锁存器可能是未定义的。

不知道有没有时间更新论文,但我可以确保下一个版本是固定的。

阿拉斯代尔

【讨论】:

  • 欢迎来到 StackOverflow,感谢您公开回复。如果您能在下一个版本可用时跟进一个链接,那就太好了。
  • 7 月份在圣克拉拉举行了一次额外的 SG1 会议 - 我已经在 SG1 wiki 上发布了一篇更新的论文,我希望它会被发布(以及来自圣诞老人的任何其他更改)克拉拉)在芝加哥前的邮件中。
猜你喜欢
  • 2019-11-30
  • 1970-01-01
  • 2020-09-13
  • 1970-01-01
  • 2016-01-19
  • 1970-01-01
  • 2010-11-21
  • 2014-11-08
  • 1970-01-01
相关资源
最近更新 更多