C++ 使用单独的线程从文件预加载数据

C++ 使用单独的线程从文件预加载数据,c++,multithreading,c++11,C++,Multithreading,C++11,我有一个处理大量(相对较小)文件的小应用程序。它按顺序运行:从文件加载数据,对其执行操作,然后移动到下一个文件。 我注意到,在运行时,CPU使用率不是100%,我猜这是由于硬盘上的I/O操作所花费的时间 因此,我们的想法是使用一个单独的线程(所讨论的数据只是一个int序列,存储在一个向量中),在处理当前数据的同时并行加载内存中的下一个数据。这似乎是一个非常常见的问题,但我很难找到一个简单的C++例子来实现这一点! 现在C++0x即将推出,一个简单的演示代码使用新的线程工具,没有外部库,将是非常好

我有一个处理大量(相对较小)文件的小应用程序。它按顺序运行:从文件加载数据,对其执行操作,然后移动到下一个文件。 我注意到,在运行时,CPU使用率不是100%,我猜这是由于硬盘上的I/O操作所花费的时间

因此,我们的想法是使用一个单独的线程(所讨论的数据只是一个int序列,存储在一个向量中),在处理当前数据的同时并行加载内存中的下一个数据。这似乎是一个非常常见的问题,但我很难找到一个简单的C++例子来实现这一点! 现在C++0x即将推出,一个简单的演示代码使用新的线程工具,没有外部库,将是非常好的

此外,尽管我知道这取决于很多事情,但是否有可能对这种方法的好处(或挫折)有一个有根据的猜测,例如要加载的数据文件的大小?我猜对于大文件,磁盘I/O操作很少,因为数据已经被缓冲(使用fstream(?)


Olivier

由于您的文件大小相对较小,并且必须处理大量文件,因此更好的设计是创建两个线程

1.第一个线程只读取和处理以偶数放置的文件 在文件列表中(ls-l in*nix)。 2.第二个线程读取列表中奇怪放置的文件。

您提到的“一个线程将数据读入一个向量,另一个线程从中读取数据”的方法的缺点是,您必须关注线程争用,并且需要使用互斥和条件变量来防止争用

其中,由于此方法不需要任何锁定[希望文件之间的数据之间没有依赖关系]

此外,从文件中读取数据的更快方法是将文件二进制读取到适当大小的缓冲区中

希望答案对你有帮助。

**编辑:** 根据您的评论,似乎您必须使用一个线程将数据读入队列数据结构[可能是一个字符缓冲区队列],第二个线程从队列读取数据并进行处理。

如前所述,问题在于从同一队列进行读写,因为STL容器不是线程安全的。

因此,我在此建议您管理共享数据结构,即使用locaks在此处排队,以及其他所有方法: 1. Boost Lock free : Boost lock free 2. Write your own loack free implementation : Lock free impl 1.无增压锁: 2.编写自己的无Lock实现:


我将创建两个线程和两个缓冲区:

  • 首先,谁将数据从文件读取到缓冲区
  • 第二,谁处理收到的数据
如果文件不适合缓冲区,只需添加文件结束标志。如果第二个线程在缓冲区的末尾没有找到它,它应该从第二个线程读取它

缓冲区的数量和大小以及线程的数量可能是需要优化的参数。 其主要思想是让磁盘控制器连续工作

**编辑**


理想的情况是,所有的执行时间都花在从HDD读取数据上。但是,它取决于“每个基准部分的继续时间”/“每个基准部分的硬盘读取时间”,因为这可能会发生变化。

对于这样的IO限制问题,使用线程将使您获得微不足道的性能增益。您可以通过提前打开多个文件,并通过线程重复系统调用(如您所示),来填补一些“空白”,以使可用IO资源饱和

相反,我建议您查看如何执行IO的内核提示,这将改进预读,并提高物理读取带宽,例如通过验证文件系统、内核和硬盘驱动器(或任何存储源)是否尽可能快


    • 一个关于如何使用一些C++0x线程和同步功能的玩具程序。不知道这有什么表现(我推荐Matt的答案),我的重点是清晰和正确,以便举例说明

      根据您的要求,将单独读取这些文件。但是,它们没有转换为
      int
      序列,因为我觉得这与处理有关,而不是严格的I/O。因此,文件被转储到一个普通的
      std::string

      #include <fstream>
      #include <sstream>
      #include <string>
      #include <vector>
      #include <deque>
      #include <future>
      #include <mutex>
      #include <condition_variable>
      
      int
      main()
      {
          // this is shared
          std::mutex mutex;
          std::condition_variable condition;
          bool more_to_process = true;
          std::deque<std::string> to_process;
      
          /* Reading the files is done asynchronously */
          std::vector<std::string> filenames = /* initialize */
          auto process = std::async(std::launch::async, [&](std::vector<std::string> filenames)
          {
              typedef std::lock_guard<std::mutex> lock_type;
              for(auto&& filename: filenames) {
                  std::ifstream file(filename);
                  if(file) {
                      std::ostringstream stream;
                      stream << file.rdbuf();
                      if(stream) {
                          lock_type lock(mutex);
                          to_process.push_back(stream.str());
                          condition.notify_one();
                      }
                  }
              }
              lock_type lock(mutex);
              more_to_process = false;
              condition.notify_one();
          }, std::move(filenames));
      
          /* processing is synchronous */
          for(;;) {
              std::string file;
              {
                  std::unique_lock<std::mutex> lock(mutex);
                  condition.wait(lock, [&]
                  { return !more_to_process || !to_process.empty(); });
      
                  if(!more_to_process && to_process.empty())
                      break;
                  else if(to_process.empty())
                      continue;
      
                  file = std::move(to_process.front());
                  to_process.pop_front();
              }
      
              // use file here
          }
      
          process.get();
      }
      
      #包括
      #包括
      #包括
      #包括
      #包括
      #包括
      #包括
      #包括
      int
      main()
      {
      //这是共享的
      std::互斥互斥;
      std::条件\可变条件;
      bool more_to_process=true;
      std::deque to_过程;
      /*读取文件是异步完成的*/
      std::vector文件名=/*初始化*/
      自动进程=std::async(std::launch::async,[&](std::vector文件名)
      {
      typedef std::锁紧装置锁紧装置锁紧装置;
      用于(自动&文件名:文件名){
      std::ifstream文件(文件名);
      如果(文件){
      std::奥斯汀溪流;
      
      流我没有明确提到它,但我假设数据之间存在依赖关系,或者至少文件必须按特定顺序处理。否则,您建议的策略确实非常明智,正如您所指出的,它避免了处理互斥锁的麻烦…好吧,我想这很好地回答了我的问题!非常好很好的一段代码。谢谢。看起来很有趣。但是我看不出它是如何与STL(fstream)集成的。而且,对这些函数的调用看起来是阻塞的。
      #include <fstream>
      #include <sstream>
      #include <string>
      #include <vector>
      #include <deque>
      #include <future>
      #include <mutex>
      #include <condition_variable>
      
      int
      main()
      {
          // this is shared
          std::mutex mutex;
          std::condition_variable condition;
          bool more_to_process = true;
          std::deque<std::string> to_process;
      
          /* Reading the files is done asynchronously */
          std::vector<std::string> filenames = /* initialize */
          auto process = std::async(std::launch::async, [&](std::vector<std::string> filenames)
          {
              typedef std::lock_guard<std::mutex> lock_type;
              for(auto&& filename: filenames) {
                  std::ifstream file(filename);
                  if(file) {
                      std::ostringstream stream;
                      stream << file.rdbuf();
                      if(stream) {
                          lock_type lock(mutex);
                          to_process.push_back(stream.str());
                          condition.notify_one();
                      }
                  }
              }
              lock_type lock(mutex);
              more_to_process = false;
              condition.notify_one();
          }, std::move(filenames));
      
          /* processing is synchronous */
          for(;;) {
              std::string file;
              {
                  std::unique_lock<std::mutex> lock(mutex);
                  condition.wait(lock, [&]
                  { return !more_to_process || !to_process.empty(); });
      
                  if(!more_to_process && to_process.empty())
                      break;
                  else if(to_process.empty())
                      continue;
      
                  file = std::move(to_process.front());
                  to_process.pop_front();
              }
      
              // use file here
          }
      
          process.get();
      }