如何可靠地结束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) { 
//...
}
}
};

有什么标准的方法可以做到这一点吗?还是我的方法(永远有一条线索(错了?如果是C的话,我会杀了线程。。。

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

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

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

如果您想实现此解决方案并继续使用std::istream进行输入,那么您可能需要考虑派生自己的std::streambuf类,该类实现成员函数underflow,使其首先调用平台特定函数之一poll/epoll/WaitForMultipleObjects,以等待新的输入变为可用或退出事件变为信号。如果发出退出事件的信号,则函数underflow应返回Traits::eof(),这将导致std::istream中的eofbit被设置,并且std::getline立即返回。否则,一旦报告有新的输入可用,就可以调用特定于平台的函数之一read/ReadFile来填充std::streambuf对象的get区域,并根据需要调整对象的指针。

最新更新