简体   繁体   中英

How to reliably end a thread blocked on an IO task

I have a class which executes a thread in order to constantly read lines from a given istream, which are then parsed internally. At some point I want it to end, but since the getline() call is blocking, it may wait forever on 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) { 
            //...
        }

    }
};

Is there any standard way to accomplish this? Or is my approach (have a thread reading forever) wrong? If it were CI would just kill the thread...

If std::getline encounters end-of-file, then it will immediately return and stop blocking. Therefore, if you could somehow arrange for this to happen when you want the thread to exit, then that would probably be the best solution. However, if that is not possible, then I'm afraid that ISO C++ itself does not offer any way to solve the problem.

But most platforms offer platform-specific extensions which allow you to wait on more than one kernel object at once. For example, Linux offers poll and epoll which allows you to wait for input on a file descriptor and to wait for an event object in the same function call (actually, Linux considers event objects also to be file descriptors). Microsoft Windows offers similar functionality with WaitForMultipleObjects .

You could create an event object (using eventfd on Linux, CreateEvent on Windows) and set this event object to signalled when you want your thread to cancel the wait and to quit. If the thread is waiting for either the event object to become signalled or for new input on the file descriptor, then it will stop waiting as soon as the event becomes signalled. That way, you will no longer have the problem of the thread blocking while waiting for new input on the file descriptor.

If you want to implement this solution and continue using std::istream for input, then you may want to consider deriving your own std::streambuf class which implements the member function underflow in such a way that it first calls one of the platform-specific functions poll / epoll / WaitForMultipleObjects to wait for either new input to become available or for the quit event to become signalled. If the quit event is signalled, then the function underflow should return Traits::eof() , which will cause the eofbit in std::istream to be set and std::getline to return immediately. Otherwise, as soon as new input is reported to be available, you can call one of the platform-specific functions read / ReadFile to fill the get area of the std::streambuf object, adjusting the pointers of the object as necessary.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM