我在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);
}
};
感谢您的帮助!
答案 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
不会泄漏,它会读入您已分配的缓冲区并且您知道该缓冲区。它要么成功要么失败(或阻塞,但信号会解锁它)。如果确实成功,请查看您可以对数据做什么(它可能仍然是部分读取或损坏或其他),否则重复直到running
为false
。
然后清理,释放你分配的内容,然后优雅地退出主题(...或做你想做的任何事情)。
如果我没有弄错,在收到一些数据之前和之后相当复杂“在安装处理程序而不提供SA_RESTART
时,根本不会出现关于重新启动系统调用的情况。
但即使这样,如果确实出现了,谁在乎 - 最终,系统调用只能失败或成功,read
即使没有信号, 总是返回部分数据,所以你必须始终为此做好准备。因此,你真的只对你是否已经收集到足够的数据以使它有用的东西感兴趣,或者running
标志是否由于某种原因“神奇地”成为false
感兴趣(原因是你的信号处理程序)。