I'm using a C++11 std::thread. Its main loop consists of a blocking recvfrom() call which listens for UDP packets arriving on a DATAGRAM socket, and some sophisticated code that parses the message and manipulates loads of STL containers in the process.

我正在使用C ++ 11 std :: thread。它的主循环包括一个阻塞的recvfrom()调用,它监听到达DATAGRAM套接字的UDP数据包,以及一些复杂的代码,用于解析消息并在进程中处理大量的STL容器。

The thread belongs to a class (helloexchange), is started by the constructor and should be cancelled in the destructor. For obvious reasons, I do not want to forcefully terminate the thread, since this could currupt the data structures, which are partially located outside the class.

该线程属于一个类(helloexchange),由构造函数启动,应该在析构函数中取消。出于显而易见的原因,我不想强​​行终止线程,因为这可能会破坏数据结构,这些数据结构部分位于类之外。

When using pthread instead of std::thread, there is the pthread_cancel method, which, in conjunction with pthread_setcancelstate, provides all the functionality I require: It will only cancel the thread while it's blocking in certain system calls, and the cancelling can be completely disabled for certain sections. The cancel is then performed once it's enabled again. This is a complete example of working pthread code:

当使用pthread而不是std :: thread时,有pthread_cancel方法,它与pthread_setcancelstate一起提供了我需要的所有功能:它只会在某些系统调用阻塞时取消该线程,并且取消可以完全取消对某些部分禁用。一旦再次启用取消,则执行取消。这是工作pthread代码的完整示例:

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

However, std::thread does not support cancel, nor does std::this_thread support setcancelstate (You'll find a reference here). It does, however, support native_handle, which returns the internally used pthread_t id. The obvious approach of just sending pthread_cancel() to the thread's native handle results in a segmentation fault, though:

但是,std :: thread不支持cancel,std :: this_thread也不支持setcancelstate(你会在这里找到一个引用)。但它确实支持native_handle,它返回内部使用的pthread_t id。然而,将pthread_cancel()发送到线程的本机句柄的明显方法会导致分段错误:

#include <iostream>
#include <thread>
#include <cstdio>

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

int sock;

void tfun() {
    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);

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

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

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;
    }
    //the listener thread
    std::thread *t = new std::thread(&tfun);
    //wait
    std::cin.get();
    //cancel the listener thread. pthread_cancel does not block.
    std::cout << "Cancelling thread" << std::endl;
    if(pthread_cancel(t->native_handle()) != 0) {
        perror("Could not cancel thread");
    }
    //join (blocks until the thread has actually cancelled).
    std::cout << "Joining thread" << std::endl;
    t->join();
    delete t;
    //close socket
    if(close(sock) != 0) {
        perror("Could not close socket");
    };
}

results in:

(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

Is there any way to cancel a std::thread while it's blocking in a syscall?

有什么方法可以在系统调用中阻止std :: thread吗?

edit

I'm not asking for a cross-platform solution; a POSIX-conforming solution would suffice.

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

2 个解决方案

#1


3

I am going to suggest a workaround along the lines of well-known self-pipe trick for unblocking select(2) and sidestep the whole messy thread cancellation business.

我将建议一个解决方法,沿着众所周知的自管道技巧来解锁select(2)并避开整个凌乱的线程取消业务。

Since you know the IP address and the port of the socket(7) your thread is blocking on, just sendto(2) some well-known sentinel packet to it from your main thread that would indicate that it's time to break out of the loop.

既然您知道线程阻塞的套接字(7)的IP地址和端口,只需从主线程发送到(2)一些众所周知的Sentinel数据包,这表明它是时候突破循环了。

This way you don't have to subvert the std::thread abstraction and can stay reasonably portable.

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

Edit 0:

If you don't like workaround, call it a technique :)

如果您不喜欢变通方法,请将其称为技术:​​)

更多相关文章

  1. Python爬取京东评论(多线程+队列+bs4+pymysql)
  2. 利用读写锁实现sqlite多线程写的问题
  3. 线程往数据库里插数据时偶尔会报错
  4. SQLite3使用总结备忘(多线程/WAL/锁等)
  5. Android之用Handler实现主线程和子线程互相通信以及子线程和子线
  6. 《Android 创建线程源码与OOM分析》
  7. java数据结构--链表
  8. 数据结构:关于重建二叉树的三种思路
  9. 重新认识Java线程的概念

随机推荐

  1. Android(安卓)Studio下Ndk开发踩过的坑以
  2. android 数据存储之 SharedPreference
  3. Android实践之ScrollView中滑动冲突处理
  4. 2016总结+2017计划
  5. XposedHook:hook敏感函数
  6. Android(安卓)反编译
  7. 如何把Eclipse工程导入到Android(安卓)St
  8. Android单元测试之Robolectric
  9. 从Android到React Native开发(一、入门)
  10. Android多线程(三)HandlerThread源码原理解