4

我正在使用多线程在 c++ 中的 linux 上进行串行 IO。目前我正在使用阻塞读取。这让我无法停止阻塞 read() 中的线程,除非强制终止或中断线程或使用诸如 pthread 取消之类的东西。现在在整个网络上,我看到人们对人们尖叫,建议他们需要从阻塞 IO 中终止线程。通常它与内存泄漏有关。只要您正确清理,线程中断是否会出现一些神奇的内存泄漏?

try
{
    while(true)
    {
        blocking_read(fd,buffer,512);
    }
}catch(interrupt_exception)
{

}
//clean up, close fd, release heap memory, usual stuff

或者是我唯一的替代方案,如下所示,或者实现更高级别的协议,以确保阻塞读取接收签名关闭输入,使其能够自行关闭。

try
{
    while(running)
    {
        nonblocking_read(fd,buffer,512);

        if(cancel)
            running = false; //break return etc
    }
}

//clean up, close fd, release heap memory, usual stuff

再说一次,如果你中断线程导致它抛出异常,在 read() 中是否会发生一些神奇的内存泄漏。

还是我应该根本不在乎并让析构函数杀死线程(我假设当您删除持有线程的对象时线程终止)?并在那里清理?像

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

谢谢你的帮助!

4

3 回答 3

6

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

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

如果使用pthread_cancel(),则会引发异常。使用这种方法,您有以下选择:

  • 通过使用 注册清理函数来执行清理pthread_cleanup_push()
  • 分配动态内存,通过pthread_setspecific().
  • 通过auto_ptr/unique_ptr.
  • 捕获abi::__forced_unwind异常,执行清理并重新抛出。

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

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

此外,删除线程对象时发生的行为取决于线程库。例如,删除pthread_tboost::thread对关联线程的执行没有影响。

于 2012-05-31T15:44:32.737 回答
1

pthread_cancel 不会直接导致神奇的内存泄漏。如果您的线程期望在明确定义的取消点处停止(有关更多详细信息,请参见 pthread_cancel 手册页),那么可以考虑到这一点进行设计。

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

-缺口

#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;
}
于 2012-05-31T14:12:58.630 回答
0

我真的只是向那个线程发送一个信号,这就是信号的用途。在你的处理程序中设置你的全局runningfalse并在你的第二个片段中有一个while(running)循环,就是这样。

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

然后清理,释放你分配的东西,然后优雅地退出线程(......或做任何你想做的事)。

如果我没记错的话,在没有提供的情况下安装处理程序时,根本不会出现关于重新启动系统调用的相当复杂的“接收一些数据之前和之后”的情况SA_RESTART
但即便如此,如果确实出现了,谁在乎 - 最后,系统调用只能失败或成功,并且即使在没有信号的情况下read总是可以返回部分数据,因此无论如何您必须始终为此做好准备。因此,您真正感兴趣的只是您是否已经收集了足够的数据以使其有用,或者running标志是否false由于某种原因“神奇地”变为(该原因将是您的信号处理程序)。

于 2012-06-01T10:19:06.417 回答