gpt4 book ai didi

c++ - 在 recvfrom 系统调用期间取消 C++11 std::thread?

转载 作者:IT王子 更新时间:2023-10-29 00:32:34 24 4
gpt4 key购买 nike

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

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

当使用 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");
};
}

但是,std::thread不支持cancelstd::this_thread也不支持setcancelstate (您会找到一个引用 here )。但是,它确实支持 native_handle,它返回内部使用的 pthread_t id。不过,将 pthread_cancel() 发送到线程的 native 句柄的明显方法会导致段错误:

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

结果:

(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 标准的解决方案就足够了。

最佳答案

我将根据众所周知的 self-pipe trick 提出一个解决方法用于解锁 select(2)并避开整个困惑的线程取消业务。

因为您知道 socket(7) 的 IP 地址和端口你的线程正在阻塞,只是 sendto(2)从您的主线程向它发送一些众所周知的哨兵数据包,这表明是时候跳出循环了。

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

编辑0:

如果您不喜欢解决方法,请将其称为技术 :)

关于c++ - 在 recvfrom 系统调用期间取消 C++11 std::thread?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12340563/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com