C++条件变量notify_one:释放锁之前还是之后?

Div*_*oML 5 c++ multithreading

以下是来自CPPConference.com的有关 C++ 条件变量的一些代码示例:

std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;

void worker_thread()
{
    // Wait until main() sends data
    std::unique_lock<std::mutex> lk(m);
    cv.wait(lk, []{return ready;});

    // after the wait, we own the lock.
    std::cout << "Worker thread is processing data\n";
    data += " after processing";

    // Send data back to main()
    processed = true;
    std::cout << "Worker thread signals data processing completed\n";

    // Manual unlocking is done before notifying, to avoid waking up
    // the waiting thread only to block again (see notify_one for details)
    lk.unlock();
    cv.notify_one();
}
Run Code Online (Sandbox Code Playgroud)

我不太明白最后在通知另一个线程之前释放锁的部分。

  1. 如果代码将 cv.notify_one() 放在 lk.unlock() 之前,它会起作用吗?
  2. 为什么将 cv.notify_one() 放在 lk.unlock() 之后是更好的做法?根据此页面,“通知线程不需要与等待线程持有的互斥锁保持相同的互斥锁;事实上,这样做是一种悲观,因为被通知的线程会立即再次阻塞,等待让通知线程释放锁。” 但是,我认为,将notify_one放在unlock之前会导致notify->unlock->另一个线程获取锁的操作,而将notify_one放在unlock之后会导致unlock->notify->另一个线程获取锁的操作。这里有什么区别呢?

Sea*_*n F 7

  1. 是的

  2. 该链接是正确的,如果通知线程拥有锁,则被通知的线程必须阻塞,直到通知线程释放锁。在多核处理器中,这是不必要的延迟。

您的比较有缺陷,因为它缺少细节。涉及两个线程,两个线程同时运行,您的比较忽略了它们。

将notify_one放在解锁之前:

notifying thread: notify -> eventually release lock
notified thread: awaken -> attempt to acquire lock and fail -> block until lock available -> acquire lock after notifying thread releases it
Run Code Online (Sandbox Code Playgroud)

将notify_one放在解锁后:

notifying thread: notify 
notified thread: awaken -> attempt to acquire lock and succeed
Run Code Online (Sandbox Code Playgroud)

  • 不。我的意思是,只要持有锁的时间超过了必要的时间,那么等待该锁的线程就会等待比必要的时间更长的时间。在单核处理器中,这还算不错,因为无论如何一次只能运行一个线程。因此整体性能保持不变,您只需更改运行时间即可。但是,当您一次可以运行多个线程(如在多核中)时,如果锁定过多,则会降低性能。 (2认同)