如何启动多个线程和每个线程处理不同的文件?

时间:2015-05-20 21:03:22

标签: c++ multithreading stdthread

我有一个单线程应用程序,它通过调用send_new_file

将文件发送到其他服务器
void send_new_file_command::start_sending_file()
{
    m_thread = thread(&send_new_file_command::execute_file, this);
}

void send_new_file_command::execute_file()
{
    for (auto it = files_need_to_send.begin(); it != files_need_to_send.end() && !is_complete(); ++it)
    {
        {
            std::unique_lock<spinning_lock> guard(lock_obj);
            m_current_file = *it;
        }
        // send a file.
        // I want to call this in parallel
        send_new_file(*it);
    }
}

有什么办法可以让多个线程和每个线程分别发送一个文件。举个例子,假设我们有4个线程,线程1,2,3,4将并行发送不同的文件。我想并行呼叫send_new_file

我正在使用std::thread。我正在研究如何在C ++中执行此操作的线程示例,但是如何在此处分解每个线程的文件数量并确保每个线程都在文件子集上工作。

  std::vector<std::thread> threads;
  for (int i = 0; i < 4; ++i)
    threads.push_back(std::thread(send_new_file(*it)));

我的背景是Java,所以稍微混淆了如何使用std :: thread在C ++中执行此操作。

3 个答案:

答案 0 :(得分:1)

第一种方法

这是第一个简单的解决方案:

  • 您的类包含要处理的文件向量
  • 只有一个线程通过函数execute_file()
  • 管理此向量
  • 此函数根据需要创建任意数量的线程,每个线程处理一个文件
  • 最后,所有线程都已连接(必填)

代码看起来像这样:

struct send_new_file_command {
    vector<string> files_need_to_send;
public:
    send_new_file_command(vector<string> f) : files_need_to_send(f) {}
    void execute_file();
};
void send_new_file_command::execute_file()
{
    vector<thread> exec;
    for(auto it = files_need_to_send.begin(); it != files_need_to_send.end(); ++it)
    {
        exec.push_back(thread(send_new_file, *it));
    }
    for(auto &e : exec)
        e.join();
}

可以使用以下代码测试代码:

void send_new_file(string x) { // simulator 
    for(int i = 0; i<10; i++) {
        cout << x << endl;
        this_thread::sleep_for(chrono::milliseconds(500));
    }
}
int main() {
    vector<string>vs{"a", "b", "c", "d"};
    send_new_file_command sfc(vs);
    sfc.execute_file();
    return 0;
}

此解决方案非常简单。它有两个主要缺点:

  • 它可能会启动比您的硬件可以管理的更多的线程。因此,只有少数几个真正同时运行。
  • 线程上的
  • 专用于文件。如果它是一个短文件并且该线程再次空闲,它将不会被重用。

其他解决方案

还有很多其他解决方案。例如:

  • 这个版本的一个变体是,它会启动固定数量的线程,每个线程一旦准备好就会查看要为下一个项目处理的文件向量。然后,您需要引入强锁定。

  • 您可以考虑期货,而不是使用原始线程,启动std::async(std::launch::async, send_new_file, *it);

答案 1 :(得分:1)

性能方面最好的方法:

  1. 使用std::atomic<int>
  2. 声明计数器变量
  3. 在向量,数组中创建线程,无论
  4. 为每个线程调用join
  5. 然后线程的主要功能访问并递增共享计数器并将结果保存在循环中的局部变量中:

    std::atomic<int> counter = 0;
    for(int j = 0;j<4;j++)
    {
        threads.push_back(std::thread([&](){
            for(int i; (i = counter++) < size;)//the counter variable must be atomic!
            {
                do_work(i);
            }
        }));
    }
    
    for(int j = 0;j<4;j++)
        threads[i].join();
    

答案 2 :(得分:1)

这是一种使用工作队列的相当简单的方法。您可以将代码片段连接到一个自包含的程序中。我们将使用以下标准库标题。

#include <fstream>
#include <iostream>
#include <mutex>
#include <string>
#include <thread>
#include <vector>

首先,我们定义一个函数,它接受一个文件名,并将它发送到任何地方。我只需将其写入/dev/null即可模拟这一点。

void
send_file(const std::string& filename)
{
  std::ifstream istr {};
  std::ofstream ostr {};
  std::string line {};
  istr.exceptions(std::ifstream::badbit);
  ostr.exceptions(std::ofstream::badbit);
  istr.open(filename);
  ostr.open("/dev/null");
  while (std::getline(istr, line))
    ostr << line << '\n';
}

接下来,我们定义一个函数,该函数获取指向仍需要发送的std::vector个文件的指针,以及另一个指向应该保护该向量的std::mutex的指针。我正在使用指针而不是引用,因为这允许我稍后创建std::thread更简单。如果你不喜欢它,你不需要这样做。

int
send_files(std::vector<std::string> *const files_p, std::mutex *const mutex_p)
{
  auto count = 0;
  while (true)
    {
      std::string next {};
      {
        const std::unique_lock<std::mutex> lck {*mutex_p};
        if (files_p->empty())  // nothing left to do
          return count;
        next = std::move(files_p->back());
        files_p->pop_back();
      }
      send_file(next);
      count += 1;
    }
}

重要的是,在执行发送文件的实际工作时,我们不保持锁定。否则,我们会彻底杀死并发。我还小心翼翼地不要在握住锁时分配任何内存。通常,您会看到std::list用作工作队列,std::condition_variable用于在发生队列更改时发出信号。我已经在another answer前发布了代码。   但是,在这个简单的情况下,队列只会被删除,因此std::vector非常适合。

最后,我们在一个简单的程序中使用我们所拥有的东西,该程序为每个硬件并发单元创建一个线程,并要求这些线程发送在命令行参数中命名的所有文件。请注意,如上所述,这将按相反的顺序处理列表。但是,如果这对你来说是个问题,那么改变是微不足道的。

int
main(int argc, char * * argv)
{
  const auto nthreads = std::thread::hardware_concurrency();
  std::mutex mutex {};
  std::vector<std::thread> threads {};
  std::vector<std::string> files {};
  files.reserve(argc - 1);
  for (auto i = 1; i < argc; ++i)
    files.push_back(argv[i]);
  threads.reserve(nthreads);
  for (auto t = 0U; t < nthreads; ++t)
    threads.emplace_back(send_files, &files, &mutex);
  for (auto t = 0U; t < nthreads; ++t)
    threads[t].join();
}