【问题标题】:Force program termination if threads block in C++如果线程在 C++ 中阻塞,则强制程序终止
【发布时间】:2021-03-10 01:31:03
【问题描述】:

当一个类负责管理一个线程时,一个常见的模式 (see for example here) 在你确定线程将及时完成后在析构函数中加入这个线程。然而,这并不总是微不足道的,正如链接线程中所概述的那样,如果执行不正确,程序将永远不会终止。下面给出了一个重现这种情况的例子:

#include <iostream>
#include <thread>
#include <chrono>
using namespace std::chrono_literals;

class Foo {
public:
    Foo() {
        mythread = std::thread([&](){
        int i = 0;
            while(running) {
                std::cout << "hi" << std::endl;
                if (i++ >= 2) {
                    // placeholder for e.g. a blocking condition variable
                    std::this_thread::sleep_for(1000h);
                }
                std::this_thread::sleep_for(500ms);
            }
        });
    }

    ~Foo() {
        running = false;
        mythread.join();
    }

private:
    std::thread mythread;
    bool running{true};
};

int main() {
    Foo bar;
    std::this_thread::sleep_for(1s);

    // enabling this line will block the termination
    //std::this_thread::sleep_for(2s);

    std::cout << "ending" << std::endl;
}

我正在寻找的是一种解决方案,如果发生这种情况,它会强制终止程序。当然,人们应该始终努力正确地完成线程,但拥有这样的功能将作为最后的手段来让内心平静,特别是对于无法观察到的嵌入式系统,崩溃程序比阻塞程序更容易恢复和调试。

一个粗略的解决方案草案是在主线程的末尾启动一个线程,该线程会休眠几秒钟,如果程序在那之后还没有结束,则调用 std::terminate(理想情况下会报告相应的错误) )。然而,我们有一个先有鸡还是先有蛋的问题,因为这个新线程当然会阻止程序及时结束。我将非常感谢任何想法。

编辑: 该解决方案不应要求修改 Foo 类本身,以便它还涵盖未修改代码中的相应错误,例如外部库。理想情况下,它甚至会涵盖没有类认为负责在主结束之前结束它们的线程(具有静态存储持续时间的类,甚至不再引用具有动态存储持续时间的对象),但如果没有深入的操作系统黑客攻击,这可能根本不可能或外部进程监视器。

【问题讨论】:

    标签: c++ multithreading


    【解决方案1】:

    有几种解决方案:

    • 调查并解决根本问题(这是最好的正确解决方案)

    解决方法:

    • 您可以通过条件变量从线程通知退出。只有在它做join之后。如果 CV 的 wait_for 返回超时 - 杀死线程(不好的解决方案,还有另一个问题)。
    • 您可以创建监视线程,它将验证时间计数器。应用程序应不时重置计数器。如果监视线程检测到时间计数器中​​的值过高,它会重新启动整个应用程序。
    • 将可疑代码移出您的应用程序以分离进程并通过 IPC 与其通信。如果出现问题 - 重新启动该应用程序(最好的解决方法)

    【讨论】:

    • 这是个好主意 (+1),我正在考虑将这种方法应用于我控制的所有线程。但是,我正在寻找一种更通用的方法,因为我还想涵盖我使用的外部库中的各个错误(无需搜索外部库中的所有线程用法并单独应用此方法)。
    • 关于编辑 1:这听起来很合理,甚至可以通过将 main 的全部内容包装在这个额外的线程中来扩展为更通用的解决方案。请参阅我对这个想法的处理方法:gist.github.com/koalo/d1b9118cc207e218685fbf6069c979b9(如果您愿意,可以将其添加到未修改或修改的答案中)。和你想的一样吗?唯一的缺点是它不涵盖具有静态存储持续时间的类,这些类在 main 完成后被破坏,但如果没有深入的操作系统黑客攻击,这可能根本不可能。
    • 关于编辑 2:这可能也有效,但在这种情况下也很棘手,因为您需要确保看门狗线程本身不会像我最初那样阻止其他类的破坏想法。
    • 我希望我能理解你所说的看门狗的意思,并像这样实现它gist.github.com/koalo/180865d4290d62de602bb3d458c398b3 但是,它也不像其他解决方案那样涵盖静态存储持续时间(无论分离看门狗线程或完成并加入它)。
    • 如果应用程序重新启动 - 静态也被释放(只有内存,但没有调用析构函数)。无论如何,如果您永远阻止调用 - 您的对象将永远不会在 OOP 方面被释放(在 C++ 中也是如此)。在阻塞线程的堆栈上创建的对象不会被破坏。在静态内存中也是如此,因为整个应用程序挂起,因为它应该等待线程终止。这个问题不在 OOP 范围内。所以,你应该选择:正确设计应用程序的面向对象或不要尝试应用 OOP/RAII 来解决此类问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-30
    • 1970-01-01
    • 2018-02-16
    相关资源
    最近更新 更多