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 来执行此操作,您可以使用信号量和一些注意等来完成此操作。
我知道这很简短,但希望它至少能为您提供一些要搜索的关键字。
有帮助的补充阅读:
- https://www.justsoftwaresolutions.co.uk/threading/locks-mutexes-semaphores.html
- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686364(v=vs.85).aspx
- https://en.wikipedia.org/wiki/Synchronization_(computer_science)#Synchronization_examples
或者查看带有任务队列的线程池。您可以将 运行s A()
的任务排队,然后在完成后将一堆 B()
任务排队(可选地让这些 B()
任务维护共享信号量或只是一个基本的线程安全计数器——InterlockedDecrement
和 Windows 上的 volatile
计数器可以使这个超级简单——监控它们的进度,所以最后一个可以重新排队 A()
运行纳尔并重复)。
假设您使用的是 C++11(或更高版本),这可以通过 std::mutex
、std::condition_variable
和 bool
来完成。一些示例代码...
#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()
我正在尝试解决以下架构,但遇到了一些麻烦。
基本上有多个线程并行访问同一个对象。第一个线程要执行 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 来执行此操作,您可以使用信号量和一些注意等来完成此操作。
我知道这很简短,但希望它至少能为您提供一些要搜索的关键字。
有帮助的补充阅读:
- https://www.justsoftwaresolutions.co.uk/threading/locks-mutexes-semaphores.html
- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686364(v=vs.85).aspx
- https://en.wikipedia.org/wiki/Synchronization_(computer_science)#Synchronization_examples
或者查看带有任务队列的线程池。您可以将 运行s A()
的任务排队,然后在完成后将一堆 B()
任务排队(可选地让这些 B()
任务维护共享信号量或只是一个基本的线程安全计数器——InterlockedDecrement
和 Windows 上的 volatile
计数器可以使这个超级简单——监控它们的进度,所以最后一个可以重新排队 A()
运行纳尔并重复)。
假设您使用的是 C++11(或更高版本),这可以通过 std::mutex
、std::condition_variable
和 bool
来完成。一些示例代码...
#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()