终止线程C ++ 11在读取时被阻止 [英] Terminate thread c++11 blocked on read

查看:77
本文介绍了终止线程C ++ 11在读取时被阻止的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码:

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.......
   }
}

我正在尝试从程序清理代码中的该线程退出。使用pthread可以工作,但是我想知道是否可以仅使用c ++ 11(没有本机句柄)做得更好。在我看来,没有使用c ++ 11代码处理所有情况的好方法。如您所见,线程在读取系统调用中被阻塞。因此,即使我清除该标志,该线程仍将被阻塞,而join调用将永远被阻塞。因此,我真正需要的是一个中断(在本例中为 pthread_cancel )。但是,如果我调用 pthread_cancel ,我将无法再调用c ++ 11 join()方法,因为它失败了,我只能调用 pthread_join() 。因此,似乎该标准有很大的局限性,我有什么遗漏吗?

I'm trying to quit from this thread in my program cleanup code. Using pthread works but I'm wondering if I can do something better with c++11 only (no native handle). It seems to me there's no good way to handle all cases using c++11 code. As you can see here the thread is blocked on a read system call. So even if I clear the flag the thread will be still blocked and join call will block forever. So what I really need is an interrupt (in this case pthread_cancel). But if I call pthread_cancel I can't call anymore the c++11 join() method because it fails, I can only call pthread_join(). So it seems the standard has a really big limitation, am I miss anything?

编辑:

讨论后下面,我更改了Foo类实现,将std :: atomic_flag替换为std :: atomic并使用了信号处理程序。我使用信号处理程序是因为我认为最好有一个通用的基类,而在基类中使用self-pipe技巧太难了,应该将逻辑委托给孩子。最终实现:

After discussion below I changed the Foo class implementation replacing std::atomic_flag with std::atomic and using signal handler. I used the signal handler because in my opinion is better to have a general base class, using the self-pipe trick is too hard in a base class, the logic should be delegated to the child. Final implementation:

#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
    }
}


推荐答案


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

As you can see here the thread is blocked on a read system call. So even if I clear the flag the thread will be still blocked and join call will block forever.

对此的解决方案是<罢工> std :: raise 这样的信号 SIGINT 编辑:您需要使用 pthread_kill ,以便信号将由正确的线程处理。正如您可以从手册中看到的那样, read 被信号打断。您必须处理 std :: signal ,否则整个过程将

The solution to this is to std::raise a signal such as SIGINT You need to raise the signal using pthread_kill so that the signal will be handled by the correct thread. As you can read from the manual, read is interrupted by signals. You must handle the std::signal or else the entire process will terminate prematurely.

在使用BSD信号处理而不是POSIX的系统上,默认情况下重新启动系统调用,而不是在中断时失败。我建议的方法取决于POSIX行为,该调用将设置 EINTR 并返回。可以使用 siginterrupt 。另一种选择是使用 sigaction 注册信号处理程序,除非有标志指定,否则不会重新启动。

On systems that use BSD signal handling instead of POSIX, system calls are by default restarted rather than failed upon interrupt. My suggested approach relies on the POSIX behaviour, where the call sets EINTR and returns. The POSIX behaviour can set explicitly using siginterrupt. Another option is to register the signal handler using sigaction, which does not restart, unless specified by a flag.

读取被中断后,必须在重试读取之前检查线程是否应该停止。

After read has been interrupted, you must check whether the thread should stop before retrying the read.


使用c ++ 11(甚至可能没有它)不要在线程中调用任何阻塞的系统调用

using c++11 (maybe even without it) don't call any blocking system call in the thread

阻止系统调用就可以了。如果您希望在不终止进程的情况下(在有限时间内)终止线程,则不应该调用 uninterruptible 系统调用,该调用可能无限期地阻塞 。我不知道该怎么做,没有系统调用符合这样的描述。

Calling blocking system calls is just fine. What you shouldn't do is call uninterruptible system calls that may block indefinitely long time, if you wish to terminate the thread without terminating the process (within finite time). Off the top of my head, I don't know if any system call matches such description.

一个最小的示例(除了无限阻止之外,其他示例都完整阅读。您可以使用 sleep(100000)进行模拟):

A minimal example (complete except for indefinitely blocking read. You can use sleep(100000) to simulate it):

#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();
}






强行杀死线程是通常是一个非常糟糕的主意,尤其是在C ++中,这可能就是为什么 std :: thread API不为其提供接口的原因。


Forcibly killing a thread is usually a very bad idea, especially in C++, which is probably why std::thread API doesn't provide interface for it.

如果您确实想杀死执行线程-在这种情况下这不是必需的,因为您可以安全地中断系统调用-那么您应该使用子进程而不是子线程。杀死子进程不会破坏父进程的堆。就是说,C ++标准库不提供进程间API。

If you really want to kill a thread of execution - which isn't necessary in this case, since you can safely interrupt the system call instead - then you should use a child process instead of child thread. Killing a child process won't break the heap of the parent process. That said, C++ standard library does not provide an inter-process API.

这篇关于终止线程C ++ 11在读取时被阻止的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆