等待线程超时:冻结

waiting for thread with timeout: freeze

本文关键字:冻结 超时 线程 等待      更新时间:2023-10-16

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

#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相关的所有其他东西)?
  • 如果一切正常,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. 通过lambda中的引用捕获传递给新线程的变量。
  2. 所有变量(m, cprocessed)都是在堆栈上分配的,当你分离工作线程时,它们将超出作用域。因此,当线程醒来时,它将访问垃圾。

因此,您需要在堆上分配数据,并确保不通过引用捕获变量。我总是喜欢将数据显式地传递给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();
}