当前位置:  开发笔记 > 编程语言 > 正文

如何加入阻塞IO的线程?

如何解决《如何加入阻塞IO的线程?》经验,为你挑选了5个好方法。

我有一个在后台运行的线程,它以阻塞的方式从输入设备读取事件,现在当我退出应用程序时我想正确地清理线程,但是我不能只运行pthread_join()因为线程由于阻塞IO,它永远不会退出.

我该如何正确解决这种情况?我应该发送pthread_kill(theard,SIGIO)还是pthread_kill(theard,SIGALRM)来打破阻塞?是其中任何一个甚至是正确的信号?或者有另一种方法来解决这种情况,并让该子线程退出阻塞读取?

目前有点困惑,因为我的谷歌搜索没有找到解决方案.

这是在Linux上并使用pthreads.

编辑:我用SIGIO和SIGALRM玩了一下,当我没有安装信号处理程序时他们打破了阻塞IO,但在控制台上给出了一条消息("I/O可能")但是当我安装一个信号处理程序时,为了避免该消息,它们不再破坏阻塞IO,因此线程不会终止.所以我回到了第一步.



1> 小智..:

执行此操作的规范方法是pthread_cancel,线程已完成pthread_cleanup_push/ pop为其正在使用的任何资源提供清理.

不幸的是,这不能在C++代码中使用.任何C++ std lib代码,或者try {} catch()当时调用堆栈上的任何内容pthread_cancel都可能会使整个进程失效.

唯一的解决方法是处理SIGUSR1,设置停止标志,pthread_kill(SIGUSR1)然后在I/O上阻塞线程的任何地方,如果EINTR在重试I/O之前检查停止标志.在实践中,这并不总是在Linux上成功,不知道为什么.

但在任何情况下,它也没用谈谈,如果你有打电话给任何第三方的lib,因为他们将最有可能有一个紧密的循环,简单地重启对I/O EINTR.反向工程他们的文件描述符以关闭它也不会削减它 - 他们可能正在等待信号量或其他资源.在这种情况下,根本不可能编写工作代码,句点.是的,这完全是脑损伤.与设计C++异常的人交谈pthread_cancel.据推测,这可以在将来的C++版本中修复.祝你好运.



2> bog..:

我也建议使用选择或其他一些非基于信号的方法来终止你的线程.我们有线程的原因之一是试图摆脱信号疯狂.那说......

通常,使用带有SIGUSR1或SIGUSR2的pthread_kill()向线程发送信号.其他建议的信号 - SIGTERM,SIGINT,SIGKILL - 具有您可能不感兴趣的进程范围的语义.

至于发送信号时的行为,我的猜测是它与你处理信号的方式有关.如果未安装处理程序,则应用该信号的默认操作,但是在接收信号的线程的上下文中.例如,SIGALRM将由您的线程"处理",但处理将包括终止进程 - 可能不是所需的行为.

线程接收信号通常会将其从EINTR的读取中分离出来,除非它确实处于前面回答中提到的那种不可中断状态.但我认为不是,或者您使用SIGALRM和SIGIO的实验不会终止该过程.

您的阅读是否可能在某种循环中?如果读取以-1返回终止,则跳出该循环并退出该线程.

你可以玩这个非常草率的代码来测试我的假设 - 我现在距离我的POSIX书有几个时区......

#include 
#include 
#include 
#include 

int global_gotsig = 0;

void *gotsig(int sig, siginfo_t *info, void *ucontext) 
{
        global_gotsig++;
        return NULL;
}

void *reader(void *arg)
{
        char buf[32];
        int i;
        int hdlsig = (int)arg;

        struct sigaction sa;
        sa.sa_handler = NULL;
        sa.sa_sigaction = gotsig;
        sa.sa_flags = SA_SIGINFO;
        sigemptyset(&sa.sa_mask);

        if (sigaction(hdlsig, &sa, NULL) < 0) {
                perror("sigaction");
                return (void *)-1;
        }
        i = read(fileno(stdin), buf, 32);
        if (i < 0) {
                perror("read");
        } else {
                printf("Read %d bytes\n", i);
        }
        return (void *)i;
}

main(int argc, char **argv)
{
        pthread_t tid1;
        void *ret;
        int i;
        int sig = SIGUSR1;

        if (argc == 2) sig = atoi(argv[1]);
        printf("Using sig %d\n", sig);

        if (pthread_create(&tid1, NULL, reader, (void *)sig)) {
                perror("pthread_create");
                exit(1);
        }
        sleep(5);
        printf("killing thread\n");
        pthread_kill(tid1, sig);
        i = pthread_join(tid1, &ret);
        if (i < 0)
                perror("pthread_join");
        else
                printf("thread returned %ld\n", (long)ret);
        printf("Got sig? %d\n", global_gotsig);

}



3> 小智..:

select()即使它很少,你也可以暂停,以便在特定条件下优雅地退出线程.我知道,民意调查很糟糕......

另一种方法是为每个子节点创建一个管道,并将其添加到线程正在监视的文件描述符列表中.当您希望该子项退出时,从父项向管道发送一个字节.不以每个线程的管道为代价进行轮询.



4> MarkR..:

取决于它如何等待IO.

如果线程处于"Uninterruptible IO"状态(顶部显示为"D"),那么你真的无能为力.线程通常只是简单地进入这种状态,做一些事情,比如等待页面被交换(或者需求加载,例如来自mmap'd文件或共享库等),但是失败(特别是NFS服务器)可能导致它会在那个州停留更长时间.

真的没有办法摆脱这种"D"状态.线程不会响应信号(您可以发送它们,但它们将排队).

如果它是普通的IO函数,如read(),write()或者像select()或poll()这样的等待函数,信号将正常传递.



5> Alexis Wilke..:

随着事态的发展,很可能得到新答案的老问题现在可用于更好地处理线程中的信号.

从Linux内核2.6.22开始,系统提供了一个新函数signalfd(),可以用来打开给定的一组Unix信号的文件描述符(在完全杀死进程的那些信号之外).

// defined a set of signals
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
// ... you can add more than one ...

// prevent the default signal behavior (very important)
sigprocmask(SIG_BLOCK, &set, nullptr);

// open a file descriptor using that set of Unix signal
f_socket = signalfd(-1, &set, SFD_NONBLOCK | SFD_CLOEXEC);

现在,您可以使用poll()select()函数来侦听您正在侦听的更常见的文件描述符(套接字,磁盘上的文件等)上的信号.

如果你想要一个可以反复检查信号和其他文件描述符的循环(即它对你的其他文件描述符也很重要),NONBLOCK很重要.

我有这样的实现,它适用于(1)定时器,(2)套接字,(3)管道,(4)Unix信号,(5)常规文件.实际上,真的是任何文件描述符加上计时器.

https://github.com/m2osw/snapcpp/blob/master/snapwebsites/libsnapwebsites/src/snapwebsites/snap_communicator.cpp
https://github.com/m2osw/snapcpp/blob/master/snapwebsites/libsnapwebsites/src/snapwebsites /snap_communicator.h

您可能也对libevent这样的图书馆感兴趣

推荐阅读
有风吹过best
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有