【问题标题】:Kill std::thread while reading a big file读取大文件时杀死 std::thread
【发布时间】:2015-02-07 17:17:41
【问题描述】:

我有一个 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);
}

这会减慢文件的读取速度吗?你有更好的主意吗?

【问题讨论】:

  • 让线程运行就行了,毕竟你不必使用结果。
  • 你不能杀死一个分离的胎面:en.cppreference.com/w/cpp/thread/thread/detach
  • @UlrichEckhardt 如果我在线程运行时删除fbuffer,程序就会崩溃。
  • 为什么你还有一个指向这样一个缓冲区的指针?为什么线程不分配、填充然后转发缓冲区?不过,我从来没有说过你应该删除缓冲区。如果您使用引用计数,只需将指针设置为 null 并忘记在后台运行的线程。当线程结束时,它会销毁它的指针副本并自动释放缓冲区。

标签: c++ multithreading c++11 fopen


【解决方案1】:

你应该不惜一切代价避免杀死一个线程。这样做会导致邪恶的事情发生,例如处于永久锁定状态的资源。

必须为线程提供一个对标志的引用,该标志的值可以从其他地方设置,以告诉线程自愿退出。

您不能为此目的使用缓冲区;如果一个线程删除缓冲区的内存,而另一个线程正在写入它,就会发生非常邪恶的事情。 (内存损坏。)所以,传递一个对布尔标志的引用。

当然,为了让线程能够定期检查标志,它必须有小块工作要做,因此将freads 分成小块是个好主意。

256 字节可能有点太小了;肯定使用 4k 或更多,甚至可能是 64k。

【讨论】:

    【解决方案2】:

    杀死线程通常不是要走的路 - 这样做 may lead to leaked resources, critical sections you cannot exitinconsistent program state.

    您的想法几乎是准确的,但您需要一种方法来指示线程完成。您可以使用在线程和线程在每次读取后读取的其余代码之间共享的布尔值,一旦设置,停止读取到缓冲区会清理文件句柄并干净地退出。

    另一方面,在现代 C++ 中,大多数时候不赞成自行处理具有拥有语义的指针的删除 - 除非您有充分的理由不这样做,否则我建议使用 stl fstream 和 string 类。

    【讨论】:

      【解决方案3】:

      您需要适当的线程同步。关于资源泄漏的 cmets 和 @Mike Nakis 关于通过设置布尔值使线程自愿退出的建议几乎是正确的(嗯,它们是正确的,但不完整)。你需要走得更远。

      您不仅必须确保加载程序线程自行退出,还必须确保在删除它正在写入的缓冲区之前它已经退出。或者,至少,您必须确保在删除缓冲区后它不会以任何方式触及该缓冲区。检查指针是否为空值不起作用有两个原因。首先,它无论如何都不起作用,因为您正在查看原始指针的副本(您必须使用指针指针或引用)。其次,更重要的是,即使检查成功,if 语句和fread 之间也存在竞争条件。换句话说,无法保证在fread 访问缓冲区时您不会释放缓冲区(无论您的块有多小)。

      至少,您需要两个布尔标志,但最好使用 正确 同步原语,例如条件变量来通知主线程(这样您就不必等待加载程序退出,但可以阻塞)。

      正确的操作方式是:

      1. 通知加载线程
      2. 等待加载器线程向我发出信号(阻塞在 cond var 上)
      3. 加载器线程获取通知,设置条件变量之后不再接触缓冲区,然后退出
      4. 恢复(删除缓冲区、分配新缓冲区等)

      如果您不坚持分离加载程序线程,您可以在告诉它退出后简单地 join 它(这样您就不需要 cond var)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-07-26
        • 2015-01-03
        • 2015-11-21
        • 1970-01-01
        • 1970-01-01
        • 2013-11-12
        • 2016-03-23
        相关资源
        最近更新 更多