在Linux C ++中终止阻塞IO

时间:2012-05-31 13:19:20

标签: c++ linux multithreading io

我在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);
    }
};

感谢您的帮助!

3 个答案:

答案 0 :(得分:6)

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

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

如果您使用read(),则会抛出异常。使用此方法,您有以下选择:

  • 通过使用pthread_cancel()注册清理功能来执行清理。
  • 分配动态内存,通过pthread_cleanup_push()存储到特定于线程的存储中。
  • 通过pthread_setspecific()管理内存。
  • 抓住auto_ptr/unique_ptr例外,执行清理并重新抛出。

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

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

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

答案 1 :(得分:1)

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

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

-nick

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

答案 2 :(得分:0)

我真的只是向该线程发送信号,这就是信号的用途。在您的处理程序中将全局running设置为false,并在第二个代码段中设置while(running)循环,就是这样。

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

然后清理,释放你分配的内容,然后优雅地退出主题(...或做你想做的任何事情)。

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