【问题标题】:Is it possible to catch a exception of unjoined thread termination in c++?是否可以在 C++ 中捕获未连接线程终止的异常?
【发布时间】:2021-06-08 14:07:08
【问题描述】:

我有一个在无限循环中运行的线程来做一些工作。现在我想顺利终止程序,没有任何错误,是否可以在没有连接的情况下终止线程并捕获异常?

我尝试了以下代码:

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

using namespace std;

void foo()
{
    // simulate expensive operation
    this_thread::sleep_for(chrono::seconds(1));
}
 
void bar()
{
    // simulate expensive operation
    while (true)
        this_thread::sleep_for(chrono::seconds(1));
}
 
int main()
{
    cout << "starting first helper...\n";
    thread helper1(foo);
 
    cout << "starting second helper...\n";
    shared_ptr<thread> helper2 = make_shared<thread>(bar);
 
    cout << "waiting for helpers to finish..." << endl;
    helper1.join();

    try {
        helper2.reset(); // drop thread without join
    } catch (exception e) {
        cout << e.what() << endl;
    }
 
    cout << "done!\n";
}

我得到以下结果:

starting first helper...
starting second helper...
waiting for helpers to finish...
terminate called without an active exception
Aborted (core dumped)

看起来没有抛出异常。为什么?

【问题讨论】:

  • ~thread 调用 std::terminate 如果线程是可连接的。它不会抛出异常。销毁可连接线程是一个逻辑错误。

标签: c++ multithreading exception


【解决方案1】:

你可以传入一个原子 bool,而不是 while (true),当主线程想要退出时会改变它并加入线程:

#include <atomic>
#include <chrono>
#include <cstdio>
#include <thread>

void expensive_func() {
  std::puts("Start expensive_func");
  std::this_thread::sleep_for(std::chrono::seconds(1));
  std::puts("End expensive_func");
}

void daemon(std::atomic<bool> const* running) {
  while (*running) {
    expensive_func();
  }
}

int main() {
  std::atomic<bool> running = true;

  std::thread worker(daemon, &running);

  // Do something
  std::this_thread::sleep_for(std::chrono::seconds(5));

  // Done, let the thread know that we're done
  running = false;

  // Join the worker
  worker.join();
}

您也可以将整个事情包装在 DaemonThread 类中,这样清理会自动发生并且是异常安全的。

【讨论】:

    【解决方案2】:

    不可以,因为不是异常,线程析构函数直接调用std::terminate。

    解决方法取决于您想要什么。 如果你使用 jthread 而不是 thread,析构函数会调用 join 本身。 如果您不再需要访问该线程,您可以在销毁它之前分离该线程。

    【讨论】:

    • detach 会解决立即销毁问题,但使用它几乎总是出错。该进程不会等待线程完成,它可能随时被中断。据推测,如果你启动一个线程,你希望它做一些事情,但你不能依赖一个分离的线程来做任何事情,除非你实现自己的同步,在这种情况下你最好join 代替。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-04
    • 1970-01-01
    • 2016-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多