首页 > 解决方案 > 使用 std::thread 的多线程嵌套 for 循环

问题描述

我对 c++ 很陌生,我真的需要一些关于使用 std::thread 的多线程的建议。我有以下一段代码,它基本上使用线程将 N = 8^L 迭代(最多 8^14)的 for 循环分开:

void Lanczos::Hamil_vector_multiply(vec& initial_vec, vec& result_vec) {
result_vec.zeros();
        std::vector<arma::vec> result_threaded(num_of_threads);
        std::vector<std::thread> threads;
        threads.reserve(num_of_threads);
        for (int t = 0; t < num_of_threads; t++) {
            u64 start = t * N / num_of_threads;
            u64 stop = ((t + 1) == num_of_threads ? N : N * (t + 1) / num_of_threads);
            result_threaded[t] = arma::vec(stop - start, fill::zeros);
            threads.emplace_back(&Lanczos::Hamil_vector_multiply_kernel, this, start, stop, ref(initial_vec), ref(result_vec));
        }for (auto& t : threads) t.join();
}

其中 Lanczos 是我的通用类(实际上没有必要知道它包含什么),而成员函数 Hamil_vector_multiply_kernel 的形式为:

void Lanczos::Hamil_vector_multiply_kernel(u64 start, u64 stop, vec& initial_vec, vec& result_vec_threaded){
       // some declarations
    for (u64 k = start; k < stop; k++) {
        // some prealiminary work
        for (int j = 0; j <= L - 1; j++) {
             // a bunch of if-else statements, where result_vec_threaded(k) += something
        }
    }
}

(代码很长,所以我没有在这里粘贴整个内容)。我的问题是我在另一个函数中调用函数 Hamil_vector_multiply 100-150 次,所以我每次都创建一个新的线程向量,然后它会自行销毁。我的问题:

  1. 在调用 Hamil_vector_multiply 的函数中创建线程然后将线程向量传递给 Hamil_vector_multiply 以避免每次创建新线程是否更好?

  2. 异步攻击循环会更好吗(例如,完成迭代的第一个线程开始下一个可用的线程?如果是,你能指出任何描述异步线程的文献吗?

3)是否有更好的方法来多线程这样一个循环?(没有多线程我有一个从 k=0 到 k=N=8^14 的循环,这会占用很多时间)

  1. 我发现了几次创建线程池和作业队列的尝试,例如使用这样的工作池是否有用: https ://codereview.stackexchange.com/questions/221617/thread-pool-c-implementation

我的代码按预期工作(给出正确的结果),它提高了程序的速度,比如 16 核的 10 倍。但是,如果您有其他与多线程无关的有用意见,我将不胜感激每条建议

非常感谢您!

PS:调用 Hamil_vector_multiply 100-150 次的函数形式为:

void Lanczos::Build_Lanczos_Hamil(vec& initial_vec) {
   vec tmp(N);
   Hamil_vector_multiply(initial_vec, tmp);
   // some calculations
   for(int j=0; j<100; j++{
      // somtheing
      vec tmp2 = ...
      Hamil_vector_multiply(tmp2, tmp);
     // do somthing else  -- not related 
   }
}

标签: c++multithreadingthreadpool

解决方案


在调用 Hamil_vector_multiply 的函数中创建线程然后将线程向量传递给 Hamil_vector_multiply 以避免每次创建新线程是否更好?

如果您担心性能,是的,它会有所帮助。您现在所做的基本上是在每个函数调用中分配一个新的堆块(我说的是向量)。如果你能提前做到这一点,它会给你一些表现。这样做没有问题,但您可以获得一些性能。

异步攻击循环会更好吗(例如,完成迭代的第一个线程开始下一个可用的线程?如果是,你能指出任何描述异步线程的文献吗?

这可能不是一个好主意。在多个线程之间共享相同数据时,您必须使用互斥锁锁定资源。这意味着您将获得与使用一个线程进行处理相同的性能,因为其他线程必须等到资源解锁并准备好使用。

有没有更好的方法来多线程这样一个循环?(没有多线程我有一个从 k=0 到 k=N=8^14 的循环,这会占用很多时间)

如果你的目标是提高性能,如果你可以把它放到多个线程中,最重要的是如果多线程会有所帮助,那么没有理由不这样做。从我所见,您的实现看起来很整洁。但请记住,启动线程本身的成本有点高(与性能提升相比可以忽略不计),负载平衡肯定会进一步提高性能。

但是,如果您有其他与多线程无关的有用意见,我将不胜感激每条建议

如果每个线程的负载可能会有所不同,那么考虑负载平衡将是一项不错的投资。除此之外,我看不出有什么问题。需要改进的主要地方是你的逻辑本身。如果您的逻辑需要大量时间,线程可以做很多事情。

可选:
您可以使用它std::future来实现相同的功能,并在销毁时异步启动线程,这意味着当您的线程池销毁时(当向量超出范围时),它将启动线程。但它可能会干扰你的第一个问题。


推荐阅读