如何检查 std::thread 是否仍在运行?

新手上路,请多包涵

如何检查 std::thread 是否仍在运行(以独立于平台的方式)?它缺少 timed_join() 方法,而 joinable() 不是为了那个。

我想在线程中用 std::lock_guard 锁定一个互斥锁,并使用互斥锁的 try_lock() 方法来确定它是否仍然被锁定(线程正在运行),但这似乎是不必要的对我来说很复杂。

你知道更优雅的方法吗?

更新: 要明确:我想检查线程是否干净退出。为此目的,“挂起”线程被视为正在运行。

原文由 kispaljr 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 3.1k
2 个回答

If you are willing to make use of C++11 std::async and std::future for running your tasks, then you can utilize the wait_for function of std::future 检查线程是否仍然以这样的简洁方式运行:

 #include <future>
#include <thread>
#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono_literals;

    /* Run some task on new thread. The launch policy std::launch::async
       makes sure that the task is run asynchronously on a new thread. */
    auto future = std::async(std::launch::async, [] {
        std::this_thread::sleep_for(3s);
        return 8;
    });

    // Use wait_for() with zero milliseconds to check thread status.
    auto status = future.wait_for(0ms);

    // Print status.
    if (status == std::future_status::ready) {
        std::cout << "Thread finished" << std::endl;
    } else {
        std::cout << "Thread still running" << std::endl;
    }

    auto result = future.get(); // Get result.
}

如果您必须使用 std::thread 那么您可以使用 std::promise 来获取未来对象:

 #include <future>
#include <thread>
#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono_literals;

    // Create a promise and get its future.
    std::promise<bool> p;
    auto future = p.get_future();

    // Run some task on a new thread.
    std::thread t([&p] {
        std::this_thread::sleep_for(3s);
        p.set_value(true); // Is done atomically.
    });

    // Get thread status using wait_for as before.
    auto status = future.wait_for(0ms);

    // Print status.
    if (status == std::future_status::ready) {
        std::cout << "Thread finished" << std::endl;
    } else {
        std::cout << "Thread still running" << std::endl;
    }

    t.join(); // Join thread.
}

这两个示例都将输出:

 Thread still running

这当然是因为在任务完成之前检查线程状态。

但是话又说回来,像其他人已经提到的那样做可能会更简单:

 #include <thread>
#include <atomic>
#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono_literals;

    std::atomic<bool> done(false); // Use an atomic flag.

    /* Run some task on a new thread.
       Make sure to set the done flag to true when finished. */
    std::thread t([&done] {
        std::this_thread::sleep_for(3s);
        done = true;
    });

    // Print status.
    if (done) {
        std::cout << "Thread finished" << std::endl;
    } else {
        std::cout << "Thread still running" << std::endl;
    }

    t.join(); // Join thread.
}

编辑:

还有 std::packaged_taskstd::thread 一起使用比使用 std::promise 更清洁的解决方案:

 #include <future>
#include <thread>
#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono_literals;

    // Create a packaged_task using some task and get its future.
    std::packaged_task<void()> task([] {
        std::this_thread::sleep_for(3s);
    });
    auto future = task.get_future();

    // Run task on new thread.
    std::thread t(std::move(task));

    // Get thread status using wait_for as before.
    auto status = future.wait_for(0ms);

    // Print status.
    if (status == std::future_status::ready) {
        // ...
    }

    t.join(); // Join thread.
}

原文由 Felix Glas 发布,翻译遵循 CC BY-SA 3.0 许可协议

我最近也遇到了这个问题。尝试使用 C++20 std::jthread 使用共享停止状态检查线程是否结束,但在线程内部 std::stop_token 参数是只读的,并不表示线程完成时在外面。

所以我创建了一个简单的类( nes::uthread )扩展 std::thread 带有一个标志,表明它已经完成。例子:

 #include <atomic>
#include <chrono>
#include <iostream>
#include <memory>
#include <thread>

namespace nes {

  class uthread final
  {
    std::unique_ptr<std::atomic<bool>> m_finished;
    std::thread m_thr;

  public:
    uthread()
      : m_finished { std::make_unique<std::atomic<bool>>(true) }
    {}

    template <class Function, class... Args>
    uthread(Function&& f, Args&&... args)
      : m_finished { std::make_unique<std::atomic<bool>>(false) }
      , m_thr {
        [](std::atomic<bool>& finished, Function&& ff, Args&&... aargs) {
          try {
            std::forward<Function>(ff)(std::forward<Args>(aargs)...);
            finished = true;
          } catch (...) {
            finished = true;
            throw;
          }
        },
        std::ref(*m_finished), std::forward<Function>(f),
        std::forward<Args>(args)...
      }
    {}

    uthread(const uthread&) = delete;
    uthread(uthread&&) = default;
    uthread& operator=(const uthread&) = delete;
    uthread& operator=(uthread&&) = default;

    [[nodiscard]] std::thread::id get_id() const noexcept {
      return m_thr.get_id(); }
    [[nodiscard]] bool joinable() const noexcept { return m_thr.joinable(); }
    void join() { m_thr.join(); }
    [[nodiscard]] const std::atomic<bool>& finished() const noexcept {
      return *m_finished; }
  };

}

int main()
{
    using namespace std;
    using namespace std::chrono;
    using namespace std::chrono_literals;
    using namespace nes;

    {
      cout << "std::thread join() termination\n";

      atomic<bool> finished = false;
      thread t { [&finished] {
        this_thread::sleep_for(2s);
        finished = true;
        cout << "thread ended\n";
      }};

      for (int i = 0; i < 5; i++) {
        cout << t.get_id() << ".join() " << t.joinable()
             << " finished: " << finished << '\n';
        this_thread::sleep_for(1s);
      }

      t.join();
    }
    cout << '\n';

    {
      cout << "std::jthread join() termination\n";

      jthread t {[](stop_token st) {
        this_thread::sleep_for(2s);
        cout << "thread ended. stop possible: " << st.stop_possible() << '\n';
      }};

      auto st = t.get_stop_source();
      for (int i = 0; i < 5; i++) {
        cout << t.get_id() << ".join() " << t.joinable()
             << " finished: " << !st.stop_possible() << '\n';
        this_thread::sleep_for(1s);
      }
    }
    cout << '\n';

    {
      cout << "nes::uthread join() termination\n";

      uthread t {[] {
        this_thread::sleep_for(2s);
        cout << "thread ended\n";
      }};

      for (int i = 0; i < 5; i++) {
        cout << t.get_id() << ".join() " << t.joinable()
             << " finished: " << t.finished() << '\n';
        this_thread::sleep_for(1s);
      }

      t.join();
    }
}

可能的打印:

 std::thread join() termination
2.join() 1 finished: 0
2.join() 1 finished: 0
thread ended
2.join() 1 finished: 1
2.join() 1 finished: 1
2.join() 1 finished: 1

std::jthread join() termination
3.join() 1 finished: 0
3.join() 1 finished: 0
thread ended. stop possible: 1
3.join() 1 finished: 0
3.join() 1 finished: 0
3.join() 1 finished: 0

nes::uthread join() termination
4.join() 1 finished: 0
4.join() 1 finished: 0
thread ended
4.join() 1 finished: 1
4.join() 1 finished: 1
4.join() 1 finished: 1

您可以在 std::jthread nes::uthread 这样您就不需要加入。

原文由 Nico Engels 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题