在Linux C ++中终止阻塞IO

我使用multithreading在c ++上在Linux上进行串行IO。 目前我正在使用阻止读取。 这使我无法停止阻塞read()中的线程cought,除非强制终止或中断线程或使用像pthread取消之类的东西。 现在遍布networking,我看到人们尖叫着build议他们需要终止阻塞IO的线程。 通常它涉及的内存泄漏。 是否有一些神奇的内存泄漏,可以出现在线程中断,只要你正确清理?

try { while(true) { blocking_read(fd,buffer,512); } }catch(interrupt_exception) { } //clean up, close fd, release heap memory, usual stuff 

或者,我是唯一的替代scheme,像下面的一样,或者实现一个更高级别的协议,确保阻塞读取接收到一个签名closuresinput,使其自行closures。

 try { while(running) { nonblocking_read(fd,buffer,512); if(cancel) running = false; //break return etc } } //clean up, close fd, release heap memory, usual stuff 

所以,再次,如果你打断了线程,导致它抛出一个exception,那么在read()中是否会发生一些神奇的内存泄漏?

或者我应该只是不在乎,让析构函数杀死线程(我假设当你删除你的对象持有线程线程终止)? 并在那里清理? 喜欢

 class MyClass{ int fd; Thread* myThread; ~MyClass(){ delete myThread; close(fd); } }; 

谢谢你的帮助!

read()不应该泄漏内存。 通过阻塞读取和非阻塞读取,应用程序代码仍然负责管理作为buf参数提供的内存。 通过信号中断read()不会抛出异常,所以如果使用信号,你需要检查结果和errno

  • 如果read()在读取数据之前被信号中断,则返回-1,并将errno设置为EINTR
  • 如果在读取一些数据后read()被信号中断,则POSIX允许将errno设置为EINTR返回-1,或者对于read()返回已读取的字节数。

如果你使用pthread_cancel() ,则会抛出一个异常。 采用这种方法,您有以下选择:

  • 使用pthread_cleanup_push()注册清理函数来执行清理。
  • 分配动态内存,通过pthread_setspecific()存储到线程指定的存储器中。
  • 通过auto_ptr/unique_ptr管理内存。
  • 抓住abi::__forced_unwind异常,执行清理并重新抛出。

一般来说,考虑避免线程取消。 如果可能的话,有一个共享的标志被用来突破循环,这样更好,也更易于管理。 这允许线程执行任何必要的清理,并防止您的实现依赖于线程库的实现及其任何怪癖。

对于你的情况,你在哪里使用阻塞读取,考虑轮询fd,以查看数据是否可以通过select()进行超时,只有在fd有数据时才调用read() 。 这允许您定期检查线程标志是否设置为不再运行,并且防止您需要处理信号以从read()中断线程,因为read()不应再阻止等待数据。

此外,删除线程对象时发生的行为依赖于线程库。 例如,删除一个pthread_tboost::thread对相关线程的执行没有影响。

pthread_cancel不会直接导致魔法内存泄漏。 如果您的线程希望在明确定义的取消点处停止(请参阅pthread_cancel手册页以获取更多详细信息),那么可以根据这一点进行设计。

下面的测试程序显示valgrind没有错误,但是请注意,glibc实现取消线程时(请参阅取消和C ++异常 ),依赖生成的异常是不可移植的。

-缺口

 #include <cstdio> #include <pthread.h> #include <unistd.h> void* thread_func(void*) { try { char buf[2048]; read(1, buf, 2048); } catch(...) { fprintf(stderr, "thread %lu cancelled\n", pthread_self()); throw; } return NULL; } int main() { pthread_t thread; int res = pthread_create(&thread, NULL, thread_func, NULL); sleep(1); pthread_cancel(thread); void* tres; pthread_join(thread, &tres); return 0; } 

我真的只是发送一个信号给那个线程,那是什么信号。 在你的处理程序中将你的全局running设置为false ,并且像在你的第二个代码段中那样有一段while(running)循环,就是这样。

read不会泄漏,它会读入已经分配的缓冲区,并且您知道。 它成功或失败(或阻止,但信号将解除阻止)。 如果成功,看看你可以做的数据(它可能仍然是部分读取或损坏或其他),否则重复,直到runningfalse

然后清理,释放你已经分配的,并优雅地退出线程(…或做任何你想要的)。

如果我没有弄错,那么在安装一个处理程序而不提供SA_RESTART情况下,根本不会发生关于重新启动系统调用的相当复杂的“接收一些数据之前和之后”的情况。
但即便如此,如果出现,谁在乎 – 最终,系统调用只能失败或成功,即使在没有信号的情况下, read总是能够返回部分数据,所以您必须始终为此做好准备。 因此,你真的只关心你是否收集了足够的数据来做一些有用的事情,或者由于某种原因(这个原因将是你的信号处理器), running标志是否“神奇地”变成了false