【问题标题】:Deadlock when using condition.wait()使用 condition.wait() 时出现死锁
【发布时间】:2019-06-18 17:55:24
【问题描述】:

在此处找到代码:http://coliru.stacked-crooked.com/a/7942a18fe11ea544

我正在试验条件。等待没有超时,我发现自己陷入了僵局。我要做的事情的要点是坐在锁中,直到我确定我已经创建了一个新文件。我会设置一个标志并通知线程,然后(在我的真实程序中)将触发一个回调函数。在运行结束时,我想关闭所有线程。我将循环变量设置为 false,然后通知一个,我认为这会解除线程阻塞。我误认为这会忽略谓词评估的内容吗?

有人可以建议一个更好的线程布局来纠正死锁吗? 谢谢。

#include <iostream>
#include <string>
#include <thread>
#include <chrono>
#include <atomic>
#include <mutex>
#include <condition_variable>

using namespace std::chrono_literals;
std::atomic_bool new_file_created_{false};
std::atomic_bool run_data_logger_{false};
std::condition_variable file_monitor_condition_;
std::mutex file_monitor_mutex_;
std::thread test_thread_;

void file_monitor_thread_func()
{
    using namespace std::chrono_literals;
    while (run_data_logger_)
    {
        std::unique_lock<std::mutex> lock(file_monitor_mutex_);
        file_monitor_condition_.wait(lock, [] { return new_file_created_.load(); });
        if (new_file_created_)
        {
            std::cout<< "New File Created" << std::endl;
            new_file_created_ = false;                       
            //do some stuff
        }
        else
        {}                
    }
}

void end_the_thread()
{
    std::cout << "Ending the Thread" << std::endl;
    run_data_logger_ = false;
    file_monitor_condition_.notify_one();
    if (test_thread_.joinable())
        test_thread_.join();

    std::cout << "Thread Ended" << std::endl;
}

void trigger_new_file()
{
   new_file_created_ = true;
   file_monitor_condition_.notify_one(); 
}

void start_the_thread()
{
    run_data_logger_ = true;
    test_thread_ = std::thread(file_monitor_thread_func);

    trigger_new_file();
}

int main()
{
    for (int j = 0; j<10; j++)
    {

        start_the_thread();
        std::this_thread::sleep_for(500ms);            
        end_the_thread();
    }
}

【问题讨论】:

  • 您不能使用没有更改状态的条件和该状态的互斥锁。

标签: c++ multithreading mutex race-condition monitor


【解决方案1】:

new_file_created_ 第一次变为true 后,file_monitor_thread_func 将其重置为false,然后循环并等待它再次变为true。但是再也没有人将它设置为true

【讨论】:

  • 这是不正确的。我循环了10次。每次循环时,我都会调用 start_the_thread(),它会调用 trigger_new_file(),这会将 new_file_created_ 设置为 true。
  • 在进入第二个start_the_thread调用之前,你必须先从第一个end_the_thread调用返回,它等待第一个线程退出,等待new_file_created_变成@ 987654330@,这永远不会发生,因为第二个start_the_thread 电话永远不会到达。
  • 我确实意识到我在调用 end_the_thread 时挂在了 join() 上。我的问题是理解为什么。我的假设/理解是我可以通过调用 notify_one() 来解除阻塞条件,然后因为我已将循环变量设置为 false,我不会再次点击 condition.wait(),并将退出线程并加入。如果 notify_one() 实际上不允许 condition.wait 基于谓词解除阻塞,那么您有建议更正设计吗?
  • 您将条件传递给wait - 即return new_file_created_.load() - 这意味着只有在该条件为真时才会退出等待。这就是第二个论点的重点。 notify_one 没有说“现在离开wait”,它只是说“再次检查条件,它可能已经改变了”。在您的情况下,它没有:new_file_created_ 仍然是 false。所以wait 继续,好吧,等等。
  • 这是有道理的。即使我告诉它 notify_one(),它也会始终检查谓词返回值的有效性。它不会只是绕过它 b/c 我要求它。所以我的选择是使用 wait_for(lock, ti​​meout, predicate) 或更复杂的谓词,例如:wait(lock, [] { return new_file_created_.load() || !run_data_logger_; }
【解决方案2】:

condition_variablemutex 一起使用,这不是装饰功能。你似乎觉得

    std::unique_lock<std::mutex> lock(file_monitor_mutex_);

足以满足wait方法的前提条件;但是从概念上讲,互斥体只有在用于互斥时才有意义。您没有这样使用它,互斥锁只锁定在 file_monitor_thread_func 内,因此不能保证状态更改操作的互斥。

您使用监视器不正确的一个迹象是您觉得需要使用原子对象,因为互斥(这是先决条件)保证可以使用普通对象。

根本没有办法将条件和原子对象结合起来。它们属于不同的设计。

【讨论】:

  • 我的预期用途是阻止条件直到被唤醒或被要求解除阻止,以便它可以正确关闭。原子变量是我使用带睡眠的线程在移动到条件变量之前监视条件时的保留。我将继续根据我的要求对正确的实施进行研究。如果您对如何重新组织上面列出的代码有任何建议,我很想听听。
  • 移除原子,并使用互斥锁保护对(常规)对象的所有访问。
猜你喜欢
  • 2023-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-13
  • 1970-01-01
  • 1970-01-01
  • 2016-12-21
相关资源
最近更新 更多