使用析构函数进行线程连接

Thread joining using a destructor

这是我的问题的延续:

既然在构造函数中成功创建了线程,就知道必须加入了。由于我正在开发一个 API,我无法控制主要功能,所以我无法在那里加入它。

在 class 析构函数中加入线程是否正确,考虑到 class 的实例化对象将有一个应用程序的生命周期?

当然可以。只要确保该线程存在,否则您的程序将永远挂在该连接上。

你可以做到。但是,您是真的对启动新线程的机制感兴趣,还是对异步执行某事的效果感兴趣?如果是后者,可以移到更高级的async机制:

#include <iostream>
#include <future>


void bar()
{
    std::cout << "I'm bar" << std::endl;
}

class foo
{
public:
    foo() :
        m_f(std::async(
            std::launch::async,
            bar))
    {

    }

    ~foo()
    {
        m_f.get();
    }

private:
    std::future<void> m_f;
};

int main ()
{
    foo f;
}
  • 您要求在构造函数中异步启动 bar。您不关心自己管理线程 - 让库来处理。

  • 将结果 future 放在成员中。

  • 在dtor,get未来。

我想出了一个线程模式,我发现它可以处理 C++ 中关于 类 个线程的所有奇怪情况。

http://blog.chrisd.info/how-to-run-threads/

你遇到了 C++ RAII 的缺点之一:析构函数不能轻易报告错误,析构函数不能优雅地失败,因为它们没有 return 值并且抛出异常是 bad idea.

那么如果另一个线程没有响应停止请求,析构函数可以做什么?它可以等待更多或暴力破坏另一个线程或离开它 运行,none 其中是非常好的选择。然后它可以忽略这种情况,只记录它,或者抛出异常,尽管有应用程序立即终止的风险,这又不是一组很好的选择。

所以,你至少有 3 个选择

  • 已知加入的线程行为良好,因此您知道它会在请求时立即终止,并且在析构函数中加入是安全的。风险是,如果线程没有终止,程序将挂在连接上。
  • 您确定析构函数中可用的错误处理选项(上面已解释)足够,并且在析构函数中加入是可以的。您必须将错误检测和处理代码添加到析构函数(例如超时)。
  • 提供一个单独的 "close" 方法,它可能带有超时参数,然后是 returns 错误值或在失败时抛出异常。

对于线程,线程未能及时终止通常被认为是类似于段错误的编程错误,因此您可以选择终止应用程序并提供有帮助(对开发人员)的诊断消息(上面的第二个要点),或者只是让程序挂起(上面的第一个要点)。但这有点冒险,因为如果在路上你需要创建一个不能快速终止的线程(它正在做一个阻塞系统调用,或者它必须通知网络连接的另一端,这可能很慢) , 在锁定设计之前考虑如何解决这个问题是个好主意。