我正在构建一个运行多个线程的系统,一个线程可以将工作排队到另一个线程并等待完成。我正在使用互斥锁和condition_variables进行同步。为了避免为每个操作创建一个新的互斥锁和cv,我想优化它并尝试为每个等待的线程使用thread_local互斥锁/ cv对。然而,这出乎意料地无法正常工作,我会感到很有趣。
基本上我的代码排队工作到另一个线程并等待它看起来像:
/* thread_local */ std::mutex mtx;
/* thread_local */ std::condition_variable cv;
bool done = false;
io_service.post([&]() {
// Execute the handler in context of the io thread
functionWhichNeedsToBeCalledInOtherThread();
// Signal completion to unblock the waiter
{
std::lock_guard<std::mutex> lock(mtx);
done = true;
}
cv.notify_one();
});
// Wait until queued work has been executed in io thread
{
std::unique_lock<std::mutex> lk(mtx);
while (!done) cv.wait(lk);
}
如果同步对象不是thread_local
,则此方法可以正常工作。当我添加thread_local
时,等待的线程将永远等待,这表明条件变量永远不会发出信号。我现在感觉尽管通过引用捕获对象,但是在lambda中使用了另一个线程的thread_local对象。我甚至可以通过检查lambda内外mtx
的地址来确认捕获没有做正确的事情 - &gt;他们不匹配。
问题是:
thread_local
变量?我可以通过创建对lambda之外的thread_local
变量的显式引用并在其中使用这些引用来解决错误。但是我认为这种行为是出乎意料的,并且很想听听这是否是正确行为的解释。
答案 0 :(得分:3)
要使 mutex 工作,需要同步的每个线程都必须锁定相同的互斥锁。 thread_local
的作用是为每个线程创建不同的互斥。如果你的每个线程都有自己独立的互斥,那么它们就无法通过它们进行通信。您需要一个 互斥锁来共享所有线程。
条件变量也是如此。所有线程都需要与同一个条件变量“对话”。这意味着为每个线程都有一个单独的条件变量是没有意义的。
关于你的lambda,实例化 lambda的每个线程都将捕获它自己的thread_local
变量副本。鉴于您从lambda访问的互斥和条件变量是从另一个线程访问的,因此没有同步,因为您的lambda使用完全不同的变量集。
答案 1 :(得分:1)
您观察到的是正确的行为,因为您实际上并没有捕获任何东西。静态和线程存储持续时间对象可以直接访问,因此为了提高效率[&]
- 捕获对这些对象没有影响。但是,您可以显式捕获适当的线程本地实例:
io_service.post([&mtx = mtx, &cv = cv]() {