【问题标题】:Unlock a thread from another thread in C++11从 C++11 中的另一个线程解锁一个线程
【发布时间】:2015-07-20 14:41:10
【问题描述】:

我对 C++11 的 thread.h api 的知识并不广泛(实际上我也不熟悉线程编程,但我最近读了很多,并且知道并发和类似的东西)但我开始使用它,但我遇到了一个我从未真正遇到过的问题。

我有两个线程函数,说这些

std::thread(thread1, args); // Spawn thread 1
std::thread(thread2, args); // Spawn thread 2

[...]

int thread1(bunch of args)
{
     lock_thread_2();
     [do stuff]
     while (some condition) {
         [do stuff]
         unlock_thread_2();
     }
}

int thread2(bunch of args)
{
     while (some condition) {
         [do stuff]
         wait_for_thread1_to_unlock_me();
     }
}

我首先考虑用 std::mutex 来做,但我读到它可能很危险,因为如果我解锁一个已经解锁的互斥锁,行为是未定义的,而且在它之上它无论如何都不会像 mutex.lock 一样工作() 不一定会暂停执行(仅当互斥锁已被锁定时才会暂停),因此编写起来非常可怕,我必须将 unlock() 和 lock() 调用耦合在一起。

这里需要注意的是thread2的执行只由thread1控制,但thread2永远不会以任何方式锁定thread1。只有thread2被thread1锁定,只有thread1控制thread2的执行流程,否则没有。

您将如何以一种干净、受支持的方式做到这一点?能否举个代码示例?

谢谢!

【问题讨论】:

    标签: c++ multithreading c++11 concurrency mutex


    【解决方案1】:

    使用condition_variable

    std::condition_variable cv;
    
    int thread1(bunch of args)
    {
         [do stuff]
         while (some condition) {
             [do stuff]
             cv.notify_one();
         }
    }
    
    int thread2(bunch of args)
    {
         std::mutex mtx;
         std::unique_lock<std::mutex> lk(mtx);
    
         while (some condition) {
             [do stuff]
             cv.wait(lk);
         }
    }
    

    wait() 返回时,cv 将是 notify()-ed... 或者会有一个虚假的唤醒。为了处理后者,添加谓词通常很有帮助。

    【讨论】:

    • std::condition_variable::wait 实际上期望 std::unique_lock&lt;std::mutex&gt; &amp;Predicate 可选,因此您的示例将不起作用。
    • @dasdave 显然是的。我的示例中还包含[do stuff]
    • 虚假唤醒是一种特殊情况,在这种情况下,锁在没有任何干预的情况下神奇地自行解锁?你怎么能用“谓词”做规避呢?从我刚刚读到的谓词成员是一个函数,它依赖于一个变量来确保条件已经被有效地发出信号?
    • @Yannick Spurious wakeup。除了notify_one(),您还可以设置/增加一些变量,然后在您的谓词中检查该变量是否已设置/增加。如果不了解您的代码的更多信息,我不知道要提出什么解决方案。
    【解决方案2】:

    您可以使用std::condition_variable。它允许您通过使用notify_onenotify_all 方法分别从另一个线程通知一个线程来恢复一个或所有等待条件变量的线程。在您的代码中,您可以像这样使用它:

    std::condition_variable my_var;
    
    void thread1(args) {
        ...
        while(condition1) {
            ...
            my_var.notify_one();
        }
    }
    
    void thread2(args) {
        std::mutex mutex;
        std::unique_lock<std::mutex> lock(mutex);
        ...
        while(condition2) {
            ...
            my_var.wait(lock);
        }
    }
    

    但请注意,条件变量偶尔会出现虚假唤醒,因此您可能需要循环等待条件以检查唤醒是否有效,如下所示:

    do {
        my_var.wait(lock);
    } while(!valid_wakeup);
    

    你可以阅读更多关于std::condition_variablehere的信息。

    【讨论】:

    • 感谢您的提问。在您的第二个代码块中,我猜变量 valid_wakeup 必须在调用 notify_one() 之前由 thread1 设置,然后由 thread2 归零?
    • @Yannick 很可能是的,可能使用std::atomic 或类似的东西。
    猜你喜欢
    • 1970-01-01
    • 2023-03-21
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多