Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/153.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 取消C++;11 std::recvfrom系统调用期间的线程?_C++_Linux_C++11_Pthreads - Fatal编程技术网

C++ 取消C++;11 std::recvfrom系统调用期间的线程?

C++ 取消C++;11 std::recvfrom系统调用期间的线程?,c++,linux,c++11,pthreads,C++,Linux,C++11,Pthreads,我使用的是C++11 std::线程。它的主循环由一个blocking recvfrom()调用组成,该调用侦听到达数据报套接字的UDP数据包,以及一些复杂的代码,这些代码解析消息并在此过程中处理STL容器的负载 线程属于类(helloexchange),由构造函数启动,应该在析构函数中取消。 出于明显的原因,我不想强制终止线程,因为这可能会导致部分位于类之外的数据结构出现问题 当使用pthread而不是std::thread时,有pthread\u cancel方法,该方法与pthread\u

我使用的是C++11 std::线程。它的主循环由一个blocking recvfrom()调用组成,该调用侦听到达数据报套接字的UDP数据包,以及一些复杂的代码,这些代码解析消息并在此过程中处理STL容器的负载

线程属于类(
helloexchange
),由构造函数启动,应该在析构函数中取消。 出于明显的原因,我不想强制终止线程,因为这可能会导致部分位于类之外的数据结构出现问题

当使用
pthread
而不是
std::thread
时,有
pthread\u cancel
方法,该方法与
pthread\u setcancelstate
结合使用,提供了我需要的所有功能:它只会在某些系统调用中阻塞线程时取消线程,对于某些区段,可以完全禁用取消功能。一旦再次启用,将执行取消。 这是使用
pthread
code的完整示例:

#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <cstdio>
#include <pthread.h>
#include <net/if.h>
#include <ifaddrs.h>

int sock;

void *tfun(void *arg) {
    std::cout << "Thread running" << std::endl;
    while(true) {
        char buf[256];
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        socklen_t addrlen = sizeof(addr);
        //allow cancelling the thread
        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);

        //perform the blocking recvfrom syscall
        int size = recvfrom(sock, (void *) buf, sizeof(buf), 0,
            (struct sockaddr *) &addr, &addrlen);

        //disallow cancelling the thread
        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);

        if(size < 0) {
            perror("Could not receive packet");
            return NULL;
        } else {
            //process the packet in the most complex ways
            //you could imagine.
            std::cout << "Packet received: " << size << " bytes";
            std::cout << std::endl;
        }
    }
    return NULL;
}


int main() {
    //open datagram socket
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock < 0) {
        perror("Could not open socket");
        return 1;
    }
    //bind socket to port
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(1337);
    addr.sin_addr.s_addr = 0;
    if(bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
        perror("Could not bind datagram socket");
        return 2;
    }
    //create the listener thread
    pthread_t t;
    if(pthread_create(&t, NULL, tfun, NULL) != 0) {
        perror("Could not thread");
        return 3;
    };
    //wait
    std::cin.get();
    //cancel the listener thread. pthread_cancel does not block.
    std::cout << "Cancelling thread" << std::endl;
    if(pthread_cancel(t) != 0) {
        perror("Could not cancel thread");
    }
    //join (blocks until the thread has actually cancelled).
    std::cout << "Joining thread" << std::endl;
    if(pthread_join(t, NULL) != 0) {
        perror("Could not join thread");
    } else {
        std::cout << "Join successful" << std::endl;
    }
    //close socket
    if(close(sock) != 0) {
        perror("Could not close socket");
    };
}
结果:

(gdb) run
Starting program: /tmp/test/test-dbg 
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
[New Thread 0x7ffff6550700 (LWP 11329)]
Thread running

Cancelling thread
Joining thread

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff6550700 (LWP 11329)]
0x00007ffff6e67b45 in __gnu_cxx::__verbose_terminate_handler() () from /usr/lib/libstdc++.so.6
当一个
std::thread
在系统调用中阻塞时,有没有办法取消它

编辑


我不是在要求一个跨平台的解决方案;一个符合POSIX标准的解决方案就足够了。

我将建议一个解决方案,它遵循著名的解除阻塞和避免整个混乱的线程取消业务的思路

由于您知道线程的IP地址和端口正在阻塞,所以只需从主线程向它发送一些众所周知的sentinel数据包,这将表明是时候打破循环了

这样,您就不必破坏
std::thread
抽象,并且可以保持合理的可移植性

编辑0:
如果您不喜欢这种解决方法,可以将其称为一种技术:)

您可以通过向线程发送信号(例如SIGUSR1)来中断recvfrom。在linux上,必须禁用libc autorestart行为(有关详细信息,请参阅)才能工作

我总是发现取消线程的概念在哲学上极为可疑和不透明。“取消”执行流意味着什么?您的程序将处于何种状态?使用非阻塞I/O、插入一个事件文件描述符和一个epoll循环,并设置一个使线程返回的事件,将更加明智。(实际上,我认为Posix中的线程取消是通过在线程内发出信号来完成的,没有人喜欢信号。)AFAIK没有一种平台独立的便携式方法来取消任何旧的
std::thread
@Kerrek:当然,没有人喜欢信号(我试图手动解决这些问题,结果非常糟糕),取消后线程的状态定义得非常好manpage,它将告诉您,取消仅在定义的一组函数调用期间进行,并且仅在我通过setcancelstate允许时进行。您能否提供一个非阻塞recvmsg()的示例IO不会占用100%的CPU?@mic_e:至少在Linux中,我得出的结论是,到目前为止,做任何事情最好的方法是使用非阻塞套接字和epoll:您可以通过一个signalfd、一些timeRFD、inotifFD和eventFD进入epoll集合来处理所有通常的异步内容。网络套接字也自然地进入其中。文件描述rs最终也会如此,但前提是您有长时间、昂贵的文件操作。最重要的是,其他epoll FD可以进入epoll集合,因此您可以在内核中完成几乎所有的同步!而
pthread\u cancel
可能定义得很好-我同意@KerrekSB的观点,线程应该是协作的,而不是竞争的e、 但是,您可能有一个特定于平台的问题:Linux NPTL(pthreads)使用EH(异常处理)运行时进行取消..我已经考虑过这个解决方案,但后来放弃了它,因为(AFAIK)攻击者可以使用伪造的发送者地址发送这样的“取消”数据包。我可以设置一个内部取消标志,检查是否有其他…@KerrekSB,yes
eventfd(2)
工作,但不可移植,并假设事件解复用器类似于
select(2)
@mic_e,由于您在同一进程中,您可以从主线程翻转一些全局标志,并在从
recvfrom(2)
返回时从IO线程检查,甚至不必担心数据包内容。@NikolaiNFetissov:是的,但是标签上写着和,所以我不太担心可移植性:-)Nikolai:嗯,它相当优雅:,凯瑞克:不过,便携性应该一直是首选。。。而解决办法不应该。这是一个两难的问题。好吧,根据项目的规模,全面的非阻塞事件调度可能是有序的,但是。。。这是我的工作TM:)
(gdb) run
Starting program: /tmp/test/test-dbg 
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
[New Thread 0x7ffff6550700 (LWP 11329)]
Thread running

Cancelling thread
Joining thread

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff6550700 (LWP 11329)]
0x00007ffff6e67b45 in __gnu_cxx::__verbose_terminate_handler() () from /usr/lib/libstdc++.so.6