gpt4 book ai didi

c++ - 在condition_variable::wait()调用过程中中断程序(SIGINT),随后对exit()的调用导致程序卡住

转载 作者:行者123 更新时间:2023-12-03 13:16:35 27 4
gpt4 key购买 nike

我不确定我是否很好地理解了这个问题,所以我写了一个小例子程序来演示它:

#include <iostream>
#include <csignal>
#include <mutex>
#include <condition_variable>
#include <thread>

class Application {
std::mutex cvMutex;
std::condition_variable cv;
std::thread t2;
bool ready = false;

// I know I'm accessing this without a lock, please ignore that
bool shuttingDown = false;

public:
void mainThread() {
auto lock = std::unique_lock<std::mutex>(this->cvMutex);

while (!this->shuttingDown) {
if (!this->ready) {
std::cout << "Main thread waiting.\n" << std::flush;
this->cv.wait(lock, [this] () {return this->ready;});
}

// Do the thing
this->ready = false;
std::cout << "Main thread notification recieved.\n" << std::flush;
}
};

void notifyMainThread() {
std::cout << "Notifying main thread.\n" << std::flush;
this->cvMutex.lock();
this->ready = true;
this->cv.notify_all();
this->cvMutex.unlock();
std::cout << "Notified.\n" << std::flush;
};

void threadTwo() {
while(!this->shuttingDown) {
// Wait some seconds, then notify main thread
std::cout << "Thread two sleeping for some seconds.\n" << std::flush;
std::this_thread::sleep_for(std::chrono::seconds(3));
std::cout << "Thread two calling notifyMainThread().\n" << std::flush;
this->notifyMainThread();
}

std::cout << "Thread two exiting.\n" << std::flush;
};

void run() {
this->t2 = std::thread(&Application::threadTwo, this);
this->mainThread();

};

void shutdown() {
this->shuttingDown = true;
this->notifyMainThread();
std::cout << "Joining thread two.\n" << std::flush;
this->t2.join();
std::cout << "Thread two joined.\n" << std::flush;
// The following call causes the program to hang when triggered by a signal handler
exit(EXIT_SUCCESS);
}
};

auto app = Application();
int sigIntCount = 0;

int main(int argc, char *argv[])
{
std::signal(SIGINT, [](int signum) {
std::cout << "SIGINT recieved!\n" << std::flush;
sigIntCount++;
if (sigIntCount == 1) {
// First SIGINT recieved, attempt a clean shutdown
app.shutdown();
} else {
abort();
}
});

app.run();

return 0;
}
您可以在此处在线运行该程序: https://onlinegdb.com/Bkjf-4RHP
上面的示例是一个简单的多线程应用程序,由两个线程组成。主线程等待条件变量,直到收到通知并将 this->ready设置为 true为止。第二个线程只是定期更新 this->ready并通知主线程。最后,该应用程序在主线程上处理SIGINT ,在该线程中尝试执行干净关闭。
问题:
触发SIGINT时(通过Ctrl + C),即使在exit()中调用Application::shutdown(),应用程序也不会退出。
我认为这是正在发生的事情:
  • 主线程正在等待通知,因此被this->cv.wait(lock, [this] () {return this->ready;});阻止
  • 收到
  • SIGINT,并且wait()调用被信号中断,这导致信号处理程序被调用。
  • 信号处理程序调用Application::shutdown(),随后调用exit()。对exit()的调用会无限期挂起,因为它正在尝试进行一些清理工作,直到wait()调用恢复为止(我不确定)。

  • 我真的不确定最后一点,但这就是为什么我认为是这样的原因:
  • 当我删除对exit()中的Application::shutdown()的调用并让main()返回时,该程序将正常退出。
  • 当我用exit()替换对abort()的调用时,它对清理的作用较小,程序将退出而不会出现问题(因此表明由exit()进行的清理过程导致冻结)。
  • 如果在主线程为而不是等待条件变量时发送SIGINT,则程序将退出而不会出现问题。

  • 以上只是我遇到的问题的一个示例。就我而言,我需要在exit()中调用shutdown(),并且需要从信号处理程序中调用shutdown()。到目前为止,我的选择似乎是:
  • 将所有信号处理移到专用线程中。这将是一件很痛苦的事情,因为它将需要重写代码,以使我能够从与拥有实例的那个线程不同的线程调用Application::shutdown()Application。我还需要一种将主线程从wait()调用中拉出的方法,可能是通过向谓词添加一些OR条件。
  • 将对exit()的调用替换为对abort()的调用。这会起作用,但会导致堆栈未解卷(特别是Application实例)。

  • 我还有其他选择吗?有什么方法可以在调用std::condition_variable::wait()期间正确地中断线程,并从中断处理程序中退出程序?

    最佳答案

    正如Igor所提到的,您实际上不能在信号处理程序中做太多事情。不过,您可以对无锁原子变量进行操作,因此可以修改代码以对其进行处理。
    我已经添加了它,并进行了其他一些更改,并对我在代码中建议的更改进行了评论:

    #include <atomic>
    #include <condition_variable>
    #include <csignal>
    #include <iostream>
    #include <mutex>
    #include <thread>

    // Make sure the atomic type we'll operate on is lock-free.
    static_assert(std::atomic<bool>::is_always_lock_free);

    class Application {
    std::mutex cvMutex;
    std::condition_variable cv;
    std::thread t2;
    bool ready = false;

    static std::atomic<bool> shuttingDown; // made it atomic

    public:
    void mainThread() {
    std::unique_lock<std::mutex> lock(cvMutex);

    while(!shuttingDown) {
    // There is no need to check if(!ready) here since
    // the condition in the cv.wait() lambda will be checked
    // before it is going to wait, like this:
    //
    // while(!ready) cv.wait(lock);

    std::cout << "Main thread waiting." << std::endl; // endl = newline + flush
    cv.wait(lock, [this] { return ready; });
    std::cout << "Main thread notification recieved." << std::endl;

    // Do the thing
    ready = false;
    }
    }

    void notifyMainThread() {
    { // lock scope - don't do manual lock() / unlock()-ing
    std::lock_guard<std::mutex> lock(cvMutex);
    std::cout << "Notifying main thread." << std::endl;
    ready = true;
    }
    cv.notify_all(); // no need to hold lock when notifying
    }

    void threadTwo() {
    while(!shuttingDown) {
    // Wait some seconds, then notify main thread
    std::cout << "Thread two sleeping for some seconds." << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(3));
    std::cout << "Thread two calling notifyMainThread()." << std::endl;
    notifyMainThread();
    }
    std::cout << "Time to quit..." << std::endl;
    notifyMainThread();
    std::cout << "Thread two exiting." << std::endl;
    }

    void run() {
    // Installing the signal handler as part of starting the application.
    std::signal(SIGINT, [](int /* signum */) {
    // if we have received the signal before, abort.
    if(shuttingDown) abort();
    // First SIGINT recieved, attempt a clean shutdown
    shutdown();
    });

    t2 = std::thread(&Application::threadTwo, this);
    mainThread();

    // move join()ing out of the signal handler
    std::cout << "Joining thread two." << std::endl;
    t2.join();
    std::cout << "Thread two joined." << std::endl;
    }

    // This is made static. All instances of Application
    // will likely need to shutdown.
    static void shutdown() { shuttingDown = true; }
    };

    std::atomic<bool> Application::shuttingDown = false;

    int main() {
    auto app = Application();
    app.run();
    }

    关于c++ - 在condition_variable::wait()调用过程中中断程序(SIGINT),随后对exit()的调用导致程序卡住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64090235/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com