簡體   English   中英

通過安裝信號處理程序來關閉多線程應用程序

[英]Shutting down a multithreaded application by installing a signal handler

在下面的代碼中,我創建了一個玩具類,該類具有一個寫入隊列的線程,而另一個線程從該隊列讀取並將其打印到stdout 現在,為了徹底關閉系統,我為SIGINT設置了一個處理程序。 我期望信號處理程序設置std::atomic<bool>變量stopFlag ,這將導致threadB將毒葯(前哨)推到隊列中,遇到哪個threadA將停止的情況。

class TestClass
{
public:

    TestClass();
    ~TestClass();
    void shutDown();

    TestClass(const TestClass&) = delete;
    TestClass& operator=(const TestClass&) = delete;


private:
    void init();
    void postResults();
    std::string getResult();
    void processResults();

    std::atomic<bool> stopFlag;

    std::mutex outQueueMutex;
    std::condition_variable outQueueConditionVariable;
    std::queue<std::string> outQueue;

    std::unique_ptr<std::thread> threadA;
    std::unique_ptr<std::thread> threadB;
};

void TestClass::init()
{
    threadA = std::make_unique<std::thread>(&TestClass::processResults, std::ref(*this));
    threadB = std::make_unique<std::thread>(&TestClass::postResults, std::ref(*this));
}

TestClass::TestClass():
    stopFlag(false)
{
    init();
}

TestClass::~TestClass()
{
    threadB->join();
}

void TestClass::postResults()
{
    while(true)
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(2000));
        std::string name = "ABCDEF";
        {
            std::unique_lock<std::mutex> lock(outQueueMutex);
            outQueue.push(name);
            outQueueConditionVariable.notify_one();
        }
        if(stopFlag)
        {
            /*For shutting down output thread*/
            auto poisonPill = std::string();
            {
                std::unique_lock<std::mutex> lock(outQueueMutex);
                outQueue.push(poisonPill);
                outQueueConditionVariable.notify_one();
            }
            threadA->join();
            break;
        }
    }
}

void TestClass::shutDown()
{
    stopFlag = true;
}

std::string TestClass::getResult()
{
    std::string result;
    {
        std::unique_lock<std::mutex> lock(outQueueMutex);
        while(outQueue.empty())
        {
            outQueueConditionVariable.wait(lock);
        }
        result= outQueue.front();
        outQueue.pop();
    }
    return result;
}

void TestClass::processResults()
{
    while(true)
    {
        const auto result = getResult();

        if(result.empty())
        {
            break;
        }

        std::cout << result << std::endl;

    }
}

static void sigIntHandler(std::shared_ptr<TestClass> t, int)
{
    t->shutDown();
}
static std::function<void(int)> handler;

int main()
{
    auto testClass = std::make_shared<TestClass>();
    handler = std::bind(sigIntHandler, testClass, std::placeholders::_1);
    std::signal(SIGINT, [](int n){ handler(n);});
    return 0;
}

我使用-std = c ++ 14標志使用gcc 5.2對此進行了編譯。 在CentOS 7計算機上按Ctrl-C時,出現以下錯誤,

terminate called after throwing an instance of 'std::system_error'
  what():  Invalid argument
Aborted (core dumped)

請幫助我了解發生了什么。

在您的平台上,當出現實際SIGINT信號時,將調用此信號處理程序。 可以在此信號處理程序內部調用的函數列表相當有限,調用任何其他函數都會導致未定義的行為。

發生的情況是您的main功能立即退出,先銷毀全局handler對象,然后銷毀testClass 然后主線程在TestClass::~TestClass被阻塞。 信號處理程序最終訪問已毀壞的對象,這導致不確定的行為。

根本原因是由於共享指針導致的對象所有權不確定-您不知道什么以及何時破壞對象。


一種更通用的方法是使用另一個線程來處理所有信號,並阻塞所有其他線程中的信號。 然后,該信號處理線程可以在接收到信號后調用任何函數。

您也完全不需要智能指針和函數包裝器。

例:

class TestClass
{
public:
    TestClass();
    ~TestClass();
    void shutDown();

    TestClass(const TestClass&) = delete;
    TestClass& operator=(const TestClass&) = delete;

private:
    void postResults();
    std::string getResult();
    void processResults();


    std::mutex outQueueMutex;
    std::condition_variable outQueueConditionVariable;
    std::queue<std::string> outQueue;
    bool stop = false;

    std::thread threadA;
    std::thread threadB;
};

TestClass::TestClass()
    : threadA(std::thread(&TestClass::processResults, this))
    , threadB(std::thread(&TestClass::postResults, this))
{}

TestClass::~TestClass() {
    threadA.join();
    threadB.join();
}

void TestClass::postResults() {
    while(true) {
        std::this_thread::sleep_for(std::chrono::milliseconds(2000));
        std::string name = "ABCDEF";
        {
            std::unique_lock<std::mutex> lock(outQueueMutex);
            if(stop)
                return;
            outQueue.push(name);
            outQueueConditionVariable.notify_one();
        }
    }
}

void TestClass::shutDown() {
    std::unique_lock<std::mutex> lock(outQueueMutex);
    stop = true;
    outQueueConditionVariable.notify_one();
}

std::string TestClass::getResult() {
    std::string result;
    {
        std::unique_lock<std::mutex> lock(outQueueMutex);
        while(!stop && outQueue.empty())
            outQueueConditionVariable.wait(lock);
        if(stop)
            return result;
        result= outQueue.front();
        outQueue.pop();
    }
    return result;
}

void TestClass::processResults()
{
    while(true) {
        const auto result = getResult();
        if(result.empty())
            break;
        std::cout << result << std::endl;
    }
}

int main() {
    // Block signals in all threads.
    sigset_t sigset;
    sigfillset(&sigset);
    ::pthread_sigmask(SIG_BLOCK, &sigset, nullptr);

    TestClass testClass;

    std::thread signal_thread([&testClass]() {
        // Unblock signals in this thread only.
        sigset_t sigset;
        sigfillset(&sigset);
        int signo = ::sigwaitinfo(&sigset, nullptr);
        if(-1 == signo)
            std::abort();

        std::cout << "Received signal " << signo << '\n';
        testClass.shutDown();
    });

    signal_thread.join();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM