代码之家  ›  专栏  ›  技术社区  ›  greywolf82

终止线程C++ 11在读上被阻止

  •  3
  • greywolf82  · 技术社区  · 6 年前

    我有以下代码:

    class Foo {
    private:
        std::thread thread;
        void run();
        std::atomic_flag running;
        std::thread::native_handle_type native;
    public:
        Foo(const std::string& filename);
        virtual ~Foo();
        virtual void doOnChange();
        void start();
        void quit();
    };
    
    #include "Foo.h"
    #include <functional>
    
    #include <iostream>
    
    Foo::Foo(const std::string& filename) :
            thread(), running(ATOMIC_FLAG_INIT) {
        file = filename;
        native = 0;
    }
    
    Foo::~Foo() {
        quit();
    }
    
    void Foo::start() {
        running.test_and_set();
        try {
            thread = std::thread(&Foo::run, this);
        } catch (...) {
            running.clear();
            throw;
        }
        native = thread.native_handle();
    }
    
    void Foo::quit() {
        running.clear();
        pthread_cancel(native);
        pthread_join(native, nullptr);
        //c++11-style not working here
        /*if (thread.joinable()) {
            thread.join();
            thread.detach();
        }*/
    }
    
    void Foo::run() {
       while (running.test_and_set()) {
            numRead = read(fd, buf, BUF_LEN);
            .....bla bla bla.......
       }
    }
    

    我正试图在程序清理代码中退出此线程。使用pTrand作品,但我想知道我是否可以做一些更好的与C++ 11只(没有本地句柄)。在我看来,用C++ 11代码处理所有的案例都是不好的。如您所见,线程在读取系统调用时被阻塞。因此,即使清除了标记,线程仍将被阻塞,而join调用将永远阻塞。所以我真正需要的是一个中断(在本例中 pthread_cancel )但是如果我打电话 线程删除 我不能再调用C++ 11连接()方法,因为它失败了,我只能调用 pthread_join() . 所以这个标准似乎有很大的局限性,我会错过什么吗?

    编辑:

    在STD::原子和使用信号处理程序之后,我讨论了FoO类的实现,替换了STD::AddiICIH标志。我使用信号处理程序是因为在我看来最好有一个通用的基类,在一个基类中使用self pipe技巧太难了,逻辑应该委托给子类。最终实施:

    #include <thread>
    #include <atomic>
    
    class Foo {
    private:
        std::thread thread;
        void mainFoo();
        std::atomic<bool> running;
        std::string name;
        std::thread::native_handle_type native;
        static void signalHandler(int signal);
        void run();
    public:
        Thread(const std::string& name);
        virtual ~Thread();
        void start();
        void quit();
        void interrupt();
        void join();
        void detach();
        const std::string& getName() const;
        bool isRunning() const;
    };
    

    CPP文件:

    #include <functional>
    #include <fcntl.h>
    #include <limits.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <sys/inotify.h>
    #include <Foo.h>
    #include <csignal>
    #include <iostream>
    
    Foo::Foo(const std::string& name) :
            name(name) {
        running = false;
        native = 0;
        this->name.resize(16, '\0');
    }
    
    Foo::~Foo() {
    }
    
    void Foo::start() {
        running = true;
        try {
            thread = std::thread(&Foo::mainFoo, this);
        } catch (...) {
            running = false;
            throw;
        }
        native = thread.native_handle();
        pthread_setname_np(native, name.c_str());
    }
    
    void Foo::quit() {
        if (running) {
            running = false;
            pthread_kill(native, SIGINT);
            if (thread.joinable()) {
                thread.join();
            }
        }
    }
    
    void Foo::mainFoo() {
     //enforce POSIX semantics
     siginterrupt(SIGINT, true);
     std::signal(SIGINT, signalHandler);
        run();
        running = false;
    }
    
    void Foo::join() {
        if (thread.joinable())
            thread.join();
    }
    
    void Foo::signalHandler(int signal) {
    }
    
    void Foo::interrupt() {
        pthread_kill(native, SIGINT);
    }
    
    void Foo::detach() {
        if (thread.joinable())
            thread.detach();
    }
    
    const std::string& Foo::getName() const {
        return name;
    }
    
    bool Foo::isRunning() const {
        return running;
    }
    
    void Foo::run() {
        while(isRunning()) {
             num = read(.....);
             //if read is interrupted loop again, this time
             //isRunning() will return false
        }
    }
    
    2 回复  |  直到 6 年前
        1
  •  3
  •   eerorika    6 年前

    如您所见,线程在读取系统调用时被阻塞。因此,即使清除了标记,线程仍将被阻塞,而join调用将永远阻塞。

    解决这个问题的办法是 std::raise 一种信号,如 SIGINT 编辑:您需要使用 pthread_kill 这样信号将由正确的线程处理。从手册中可以看出, read 被信号中断。你必须处理 std::signal 否则整个过程将过早终止。

    在使用BSD信号处理而不是POSIX的系统上,系统调用在默认情况下是重新启动的,而不是在中断时失败。我建议的方法依赖于POSIX行为,其中调用 EINTR 然后返回。posix行为可以使用 siginterrupt . 另一个选项是使用 sigaction ,除非由标志指定,否则不会重新启动。

    read

    sleep(100000)

    #include <thread>
    #include <iostream>
    #include <csignal>
    #include <cerrno>
    #include <unistd.h>
    
    constexpr int quit_signal = SIGINT;
    thread_local volatile std::sig_atomic_t quit = false;
    
    int main()
    {
        // enforce POSIX semantics
        siginterrupt(quit_signal, true);
    
        // register signal handler
        std::signal(quit_signal, [](int) {
            quit = true;
        });
    
        auto t = std::thread([]() {
            char buf[10];
            while(!quit) {
                std::cout << "initiated read\n";
                int count = read(some_fd_that_never_finishes, buf, sizeof buf);
                if (count == -1) {
                    if (errno == EINTR) {
                        std::cout << "read was interrupted due to a signal.\n";
                        continue;
                    }
                }
            }
            std::cout << "quit is true. Exiting\n";;
        });
    
        // wait for a while and let the child thread initiate read
        sleep(1);
    
        // send signal to thread
        pthread_kill(t.native_handle(), quit_signal);
    
        t.join();
    }
    

    std::thread

        2
  •  4
  •   janm    6 年前

    select() poll()