C++线程:先来互斥

C++ threading: first come mutex

我正在尝试解决以下架构,但遇到了一些麻烦。

基本上有多个线程并行访问同一个对象。第一个线程要执行 Object.A(),所有其他线程应该等到该进程完成,然后在没有任何阻塞的情况下同时继续执行 Object.B()

无法在网上找到任何资源来解释任何会产生此功能的东西,并且任何 "solution" 想到的带有 2 个自定义布尔互斥锁的东西似乎都不起作用。

如果您想要并行方式,请使用条件变量为 Object.B() 启动线程。购买 Object.B() 必须是线程安全的。

NathanOliver 在评论中建议的方法是在主线程上调用 A() 然后 然后 启动其他线程,这是最简单的方法,也是第一个你应该考虑一下。

但是你正在寻找的构造称为“monitor”(你缺少的部分称为 "condition variable","monitor" 指的是整个 mutex + cond var你需要的一对)。

我不打算在这里重现示例,因为它很容易找到信息 ()。取决于您的线程子系统:

  • 对于 pthread,请参阅 condition variables
  • 对于 Windows API 见 Condition Variables as well as Event Objects,两者都可以用来完成类似的事情,我更喜欢后者,当适用于基本布尔条件变量时,除了重置事件如果您有多个线程在等待并想将它们全部唤醒,可能会变得棘手。

通用模型,对于一次性运行,是:

  • 第一个线程:
    • 致电A()
    • 当 returns 时,发出一些 "AFinished" cond var.
  • 其他线程:
    • 请稍候 "AFinished"。
    • 致电B()

如果您想重复该过程,则必须在所有 B() 为 运行 后重置条件并等待该状态,然后再调用 A()。您可以使用另一个 cond var 来执行此操作,您可以使用信号量和一些注意等来完成此操作。

我知道这很简短,但希望它至少能为您提供一些要搜索的关键字。

有帮助的补充阅读:

或者查看带有任务队列的线程池。您可以将 运行s A() 的任务排队,然后在完成后将一堆 B() 任务排队(可选地让这些 B() 任务维护共享信号量或只是一个基本的线程安全计数器——InterlockedDecrement 和 Windows 上的 volatile 计数器可以使这个超级简单——监控它们的进度,所以最后一个可以重新排队 A() 运行纳尔并重复)。

假设您使用的是 C++11(或更高版本),这可以通过 std::mutexstd::condition_variablebool 来完成。一些示例代码...

#include <condition_variable>
#include <mutex>

class Object
{
public:
    Object() :
        ready(false)
    { }

    void A()
    {
        // do preparation work here

        // Then, you set ready to true and notify all potential waiters in B:
        std::unique_lock<std::mutex> lock(protect);
        ready = true;
        ready_cond.notify_all();
    }

    void B()
    {
        if (!ready)
        {
            // Only go into blocking if A isn't done yet
            std::unique_lock<std::mutex> lock(protect);
            while (!ready)
                ready_cond.wait(lock);
        }

        // do shared work at this point
    }

private:
    std::mutex              protect;
    bool                    ready;
    std::condition_variable ready_cond;
};

这不能保护您免受多人呼叫 A、再次呼叫或任何可能发生的事情的影响,但这是一个起点。

我不打算讨论实施细节,但就方法而言,您正在寻找的是来自 The little book of semaphores 的 Barrier 解决方案。

Barrier问题就是让所有线程在一个会合点相遇然后所有继续执行关键部分(在你的情况下Object.B())。在 rendezvous 点相遇之前,只有第一个线程执行 Object.A() 并在 rendezvous 处加入线程。查看同一本书中 Reader 的作者问题,了解如何制作第一个线程 execute/call Object.A()