代码之家  ›  专栏  ›  技术社区  ›  Mert Mertce

std::unique_lock和std::condition_variable如何工作

  •  8
  • Mert Mertce  · 技术社区  · 9 年前

    我需要弄清楚锁和条件变量是如何工作的。

    在此处稍微修改的代码中 cplusplusreference

    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();
    }
    
    int main()
    {
        std::thread worker(worker_thread);
        std::this_thread::sleep_for(std::chrono::seconds(1));
    
        data = "Example data";
        // send data to the worker thread
        {
            std::lock_guard<std::mutex> lk(m);
            ready = true;
            std::cout << "main() signals data ready for processing\n";
        }
        cv.notify_one();
    
        // wait for the worker
        {
            std::unique_lock<std::mutex> lk(m);
            cv.wait(lk, []{return processed;});
        }
        std::cout << "Back in main(), data = " << data << '\n';
    
        worker.join();
    }
    

    我感到困惑的是,如果worker_thread已经锁定了互斥锁,那么主线程如何锁定互斥锁。

    从…起 this answer 我看到这是因为 cv.wait 解锁 互斥锁。

    但现在我对此感到困惑:如果 cv.wait公司 会解锁吗?

    例如,我可以这样做吗?

    std::unique_lock<std::mutex> lk(m, std::defer_lock);
    

    因此,我创建锁定对象是因为cv需要它,但我在创建时不会锁定它。

    现在有什么不同吗?

    我不明白为什么会出现“运行时错误” here 那样的话

    3 回复  |  直到 8 年前
        1
  •  9
  •   Richard Dally    9 年前

    引用自 std::condition_variable::wait() :

    如果锁定,则调用此函数。mutex()未被当前线程锁定。这是未定义的行为。

        2
  •  4
  •   Zan Lynx    9 年前

    我将尝试添加更多关于为什么条件变量需要锁的解释。

    您必须有一个锁,因为代码需要检查条件谓词是否为真。谓词是一些值或值的组合,必须为真才能继续。它可能是一个NULL指针,或指向一个已完成的数据结构,可供使用。

    在等待之前,您必须锁定它并检查谓词,因为当您开始等待条件时,另一个线程可能已经设置了它。

    条件通知和等待返回并不意味着条件为真。这只意味着这个条件在某个时候是真的。它甚至可能是真的,然后是假的,然后又是真的。这也可能意味着您的线程处于一个不相关的信号处理程序中,导致条件等待爆发。您的代码甚至不知道条件通知被调用了多少次。

    因此,一旦条件wait返回,就会锁定互斥锁。现在,您的代码可以在安全地锁定时检查条件。如果为true,则代码可以更新需要更新的内容并释放锁。如果这不是真的,它只是回到状态,等待再试一次。例如,它可以获取数据结构指针并将其复制到向量中,然后将锁保护指针设置回NULL。

    将条件视为使轮询循环更有效的方法。你的代码仍然需要做它在循环等待中所做的所有事情,除了它可以进入睡眠状态而不是不停地旋转。

        3
  •  1
  •   nitzanms    9 年前

    我认为您的误解源于对锁是什么以及它们如何与条件变量交互的更深层次的误解。

    锁存在的基本原因是提供互斥。互斥保证代码的某些部分仅由单个线程执行。这就是为什么你不能等到以后才拿着锁——你需要锁上它才能得到你的保证。

    当您希望执行代码的某些其他部分,但在当前代码段执行时仍需要互斥时,这会导致问题。这就是条件变量派上用场的地方:它们提供了一种结构化的方式来释放锁,并保证当你再次醒来时,你会把它拿回来。这就是为什么在等待功能中锁被解锁的原因。