首页 > 解决方案 > 如何可靠地结束在 IO 任务上阻塞的线程

问题描述

我有一个执行线程的类,以便不断地从给定的 istream 中读取行,然后在内部对其进行解析。在某些时候我希望它结束​​,但由于getline()呼叫被阻塞,它可能会永远等待join().

#pragma once

#include <thread>
#include <iostream>

class Parser {
private:
    std::istream& input;
    std::thread parserThread;

public:
    Parser(std::istream& input_) : input(input_)/* ... */{}
    
    ~Parser() {
        stop();
    }
    
    void start() {
        // Avoid multiple threads...
        parserThread = std::thread(&Parser::monitorThread, this);
    }
    
    
    void stop() {
        continueParsing = false;    
        parserThread.join(); // Wait for it to finish
        continueParsing = true; // Allow to start another thread at a later point
    }
    
    
private:
    void monitorThread() {
        std::string buffer;
    
        // Constantly reads new input until it's told to stop
        while(std::getline(input, buffer) && continueParsing) { 
            //...
        }

    }
};

有没有标准的方法来做到这一点?还是我的方法(永远阅读线程)错误?如果是 CI 只会杀死线程......

标签: c++

解决方案


如果std::getline遇到文件尾,则立即返回并停止阻塞。因此,如果您可以在希望线程退出时以某种方式安排这种情况发生,那么这可能是最好的解决方案。但是,如果这不可能,那么恐怕 ISO C++ 本身并没有提供任何解决问题的方法。

但是大多数平台都提供特定于平台的扩展,允许您一次等待多个内核对象。例如,Linux 提供pollepoll允许您等待文件描述符的输入并在同一个函数调用中等待事件对象(实际上,Linux 将事件对象也视为文件描述符)。Microsoft Windows 提供与WaitForMultipleObjects.

您可以创建一个事件对象(eventfd在 Linux、CreateEventWindows 上使用)并在您希望线程取消等待并退出时将此事件对象设置为发出信号。如果线程正在等待事件对象发出信号或文件描述符上的新输入,那么一旦事件发出信号,它将停止等待。这样,在等待文件描述符的新输入时,您将不再遇到线程阻塞的问题。

如果您想实现此解决方案并继续使用std::istream输入,那么您可能需要考虑派生您自己的类,std::streambuf该类实现成员函数underflow的方式是它首先调用平台特定函数之一poll//等待新的输入变为可用或退出事件发出信号。如果退出事件发出信号,那么函数应该返回,这将导致in被设置并立即返回。否则,一旦报告新输入可用,您可以调用平台特定函数之一/来填充对象的获取区域,并根据需要调整对象的指针。epollWaitForMultipleObjectsunderflowTraits::eof()eofbitstd::istreamstd::getlinereadReadFilestd::streambuf


推荐阅读