在 lambda 中通过引用捕获 thread_local 变量无法按预期工作

Capturing a thread_local variable by reference in lambda does not work as expected

我目前正在构建一个系统,其中我有多个线程 运行,一个线程可以将工作排队到另一个线程并等待完成。我正在使用互斥锁和 condition_variables 进行同步。为了避免为每个操作创建新的互斥锁和 cv,我想优化它并尝试为每个正在等待的线程使用 thread_local mutex/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 地址来确认捕获没有做正确的事情 -> 它们不匹配。

问题是:

我可以通过在 lambda 外部创建对 thread_local 变量的显式引用并在其中使用这些引用来解决该错误。但是我认为这种行为是出乎意料的,并且很想听听解释这是否是正确的行为。

要让 mutex 工作,每个需要同步的线程都必须锁定 same mutex。 thread_local 所做的是为每个线程创建一个不同的 mutex。如果您的每个线程都有自己独立的 mutex,它们就不可能通过它们进行通信。您需要 one mutex 让所有线程共享。

条件变量也是如此。所有线程都需要'talking'到同一个条件变量。这意味着为每个线程设置单独的 条件变量 没有意义。

关于您的 lambda,实例化 lambda 的每个线程都将捕获它自己的 thread_local 变量副本。鉴于您从 lambda 访问的 mutexcondition variable 是从其他线程访问的,因此没有同步,因为您的 lambda 正在使用完全不同的一组变量。

您观察到的是正确的行为,因为您实际上并没有捕捉到任何东西。静态和线程存储持续时间对象可以直接访问,因此为了提高效率 [&]-capture 对它们没有影响。但是,您可以显式捕获适当的线程本地实例:

io_service.post([&mtx = mtx, &cv = cv]() {