等待超时的线程:冻结

时间:2015-09-12 08:51:33

标签: c++ multithreading c++11

我现在什么都不懂。考虑我有下一段代码(简化版):

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

const auto k_sleep = std::chrono::seconds{3};
const auto k_wait = std::chrono::seconds{1};
const auto k_repeats = 20u;

void test()
{
    std::mutex m;
    std::condition_variable c;
    bool processed = false;

    std::thread worker{[&]() {
            std::this_thread::sleep_for(k_sleep);

            std::unique_lock<std::mutex> lock(m);
            processed = true;
            lock.unlock();
            c.notify_one();
        }};

    std::unique_lock<std::mutex> lock(m);
    if(c.wait_for(lock, k_wait, [&processed]() {
            return processed;
        }))
    {
        worker.join();
        std::cout << "done" << std::endl;
    }
    else
    {
        worker.detach();
        std::cout << "timeout" << std::endl;
    }
}

int main()
{
    for(auto i = 0u; i < k_repeats; ++i)
        test();
}

几个问题:

  • 有没有死锁?
  • 我正在使用condition_variable(以及与thread相关的所有其他内容)吗?
  • 有UB吗?
  • 如果一切正常,将会打印多少次timeout

正如您所看到的,我正在运行线程并等待它(使用condition_variable)一段时间。等待时间少于线程的执行时间。

使用VC ++(Visual Studio 2015,v 19.00.23026)和g ++(v 4.8.2),我打印了timeout 2次,然后我在调试器下卡住worker.join() 。如果我将k_sleep增加到大的东西(相对于k_wait而小循环计数),例如30秒 - 一切都会好的。

那么,为什么会这样呢?如果我做错了,请以正确的方式向我解释。感谢

1 个答案:

答案 0 :(得分:0)

你的内存管理存在问题:

  1. 通过传递给新线程的lambda中的引用捕获变量。
  2. 所有变量(mcprocessed)都在堆栈上分配,当您分离工作线程时,它们将超出范围。因此,当线程唤醒时,它将访问垃圾。
  3. 因此,您需要在堆上分配数据,并确保不通过引用捕获变量。我总是希望将数据明确传递到std::thread

    #include <iostream>
    #include <thread>
    #include <condition_variable>
    #include <mutex>
    #include <chrono>
    #include <memory>
    
    const auto k_sleep = std::chrono::seconds{ 3 };
    const auto k_wait = std::chrono::seconds{ 1 };
    const auto k_repeats = 20u;
    
    
    struct Data
    {
       std::mutex m;
       std::condition_variable c;
       bool processed = false;
    };
    
    void test()
    {
       auto data = std::make_shared<Data>();
    
       auto run = [](std::shared_ptr<Data> i_data) {
          std::this_thread::sleep_for(k_sleep);
    
          std::unique_lock<std::mutex> lock(i_data->m);
          i_data->processed = true;
          lock.unlock();
          i_data->c.notify_one();
       };
    
       std::thread worker{ run, data };
    
       std::unique_lock<std::mutex> lock(data->m);
       if (data->c.wait_for(lock, k_wait, [&data]() {
          return data->processed;
       }))
       {
          worker.join();
          std::cout << "done" << std::endl;
       }
       else
       {
          worker.detach();
          std::cout << "timeout" << std::endl;
       }
    }
    
    int main()
    {
       for (auto i = 0u; i < k_repeats; ++i)
          test();
    }