我有一个std::thread
函数调用fopen
将大文件加载到数组中:
void loadfile(char *fname, char *fbuffer, long fsize)
{
FILE *fp = fopen(fname, "rb");
fread(fbuffer, 1, fsize, fp);
flose(fp);
}
这称为:
std::thread loader(loadfile, fname, fbuffer, fsize);
loader.detach();
在某些时候,程序中的某些内容想要停止读取该文件并要求提供另一个文件。问题是,当我删除fbuffer
指针时,loader
线程仍然存在,并且我得到了一个存在异常的竞争条件。
我该如何杀死那个帖子?我的想法是检查fbuffer
的存在性,并将fread
分成小块:
void loadfile(char *fname, char *fbuffer, long fsize)
{
FILE *fp = fopen(fname, "rb");
long ch = 0;
while (ch += 256 < fsize)
{
if (fbuffer == NULL) return;
fread(fbuffer + ch, 1, 256, fp);
}
fclose(fp);
}
这会减慢文件的读取速度吗?你有更好的主意吗?
答案 0 :(得分:5)
你应该不惜一切代价避免杀死一个帖子。这样做会导致邪恶的事情发生,就像资源处于永久锁定状态一样。
必须为线程提供一个标志的引用,该标志的值可以从其他地方设置,以告诉线程自愿退出。
您不能为此目的使用缓冲区;如果一个线程在另一个线程写入时删除了缓冲区的内存,则会发生非常邪恶的事情。 (内存损坏。)因此,将引用传递给布尔标志。
当然,为了让线程能够定期检查标志,它必须有一小部分工作要做,所以将fread
分成小块是个好主意。
256字节可能有点太小了;绝对使用4k或更多,甚至可能是64k。
答案 1 :(得分:0)
通常无法杀死线程 - 执行此操作may lead to leaked resources, critical sections you cannot exit和inconsistent program state.
你的想法几乎是现场的,但你需要一种方法来表明线程最终确定。您可以使用线程与线程在每次读取后读取的其余代码之间共享的布尔值,一旦设置,停止读取缓冲区清理文件句柄并干净地退出。
另一方面,在现代C ++中,处理删除具有语义的指针大多数时候都不赞成 - 除非你有充分的理由不这样做,我建议使用stl fstream和string类。
答案 2 :(得分:0)
您需要正确的线程同步。关于资源泄漏的评论以及@Mike Nakis关于通过设置布尔值自动退出线程的提议几乎正确(好吧,他们正确,但不完整)。你需要走得更远。
您必须确保不仅加载程序线程自行退出,在删除正在写入的缓冲区之前,您必须还确保它已退出。或者,至少,您必须确保在删除缓冲区之后不会以任何方式触摸缓冲区。检查指针的null-ness不起作用有两个原因。首先,它无论如何都不起作用,因为你正在查看原始指针的副本(你必须使用指针指针或引用)。其次,更重要的是,即使检查有效,if
语句与fread
之间也存在竞争条件。换句话说,没有办法保证在fread
访问缓冲区时不会释放缓冲区(无论你的块有多小)。
至少,您需要两个布尔标志,但最好使用正确的同步原语(如条件变量)来通知主线程(因此您不必旋转等待装载机退出,但可以阻止)。
正确的操作方式是:
如果你不坚持拆卸加载器线程,你可以在告诉它退出后简单地join
它(所以你不需要cond var)。