c++运行并发的for循环,甚至可能

c++ Running a concurrent for loop, even possible?

本文关键字:循环 运行 并发 for c++      更新时间:2023-10-16

给定这个for循环:

std::vector<std::string> V={"element1","element2","element3"};
for(int i=V.size(); i--;)
{
 std::cout<<i<<std::endl;
}

是否可以同时运行for循环的每个循环?所以每个std::cout都是一次发生吗?

我有一个函数指针的向量,我需要循环并同时执行,而不是一个接一个。

注意:std::thread, std::async在这种情况下没有帮助,因为我不知道vector的大小,因此必须每个循环创建一个线程并运行它。这不会导致并发。

这是OpenMP的工作:

#pragma omp parallel for
for(int i = 0; i < V.size(); ++i)
{
    std::cout<<i<<std::endl;
}

使用一个固定的线程数(通常是CPU线程数)。如果V.size()大于它,则不会同时调用所有任务。它们将等待可用的线程。

如果你不能使用OpenMP,第三方库和框架可以帮助你。

如果你说的可执行文件是指你想要启动的单独的应用程序,那么你不需要使用线程。您只需在一个线程中一个接一个地启动可执行文件,而不必等待它们完成。你可以把它们的PID放到一个向量中。然后在单独的循环中等待,直到这些进程一个接一个地完成。

但是如果这是您在程序中唯一需要做的事情,那么您最好考虑用bash编写。

您可以并行运行这些可执行文件。这与并行运行for循环没有任何关系。它更像是操作系统的一个特性。

对于Linux,查看如何使用fork()/exec()/system()运行进程。

对于Windows,查看如何使用CreatrProcess()运行进程。

是否可以同时运行for循环的每个循环?

是的。c++标准库提供了实现并发性的底层工具。只需在每次迭代中启动一个新线程-并且在所有线程启动之前不要加入。

注意:std::thread, std::async在这种情况下没有帮助,因为我不知道vector的大小,因此必须每个循环创建一个线程并运行它。这不会导致并发。

你错了。std::thread(可能还有std::asnyc)很有用,可以用来实现这种情况下的并发性。在每个循环中创建线程并不会阻止并发。一个使用函数指针的简单演示,因为这是您真正打算使用的:

std::vector<void(*)()> function_pointers = get_the_stuff();
for(auto f : function_pointers) {
    std::thread t(f);
    t.detach();
}

另一种稍微复杂一点的方法是创建线程向量。这允许您等待,直到所有的执行加入。

,

std::thread和它的友项是用于并发的低级工具,在它们之上创建一个抽象可能是值得的——或者使用第三方提供的现有抽象。

同时,我强烈要求您重新考虑。你说你不知道向量的大小。当然,您的硬件提供商也不知道矢量。他们如何知道为您提供足够的cpu内核来并行执行所有线程?更好的方法是根据可用内核的数量使用一个固定的线程池,以避免额外线程的开销。

使用不导致并发的线程是什么意思?您是否希望所有线程立即在函数指针向量上创建线程,而不是在V上迭代并创建线程的for循环?我认为这是不可能的,很可能其他答案最终会在不同的抽象层次上做完全相同的事情。

示例代码:

#include <iostream>
#include <vector>
#include <thread>
#include <atomic>
typedef void (*func)();
std::atomic<int> atom;
void f1() { ++atom; }
void f2() { atom += 5; }
void f3() { atom -= 3; }
int main()
{
    using namespace std;
    vector<func> func_vec = { &f1, &f2, &f3 };
    vector<thread> thread_vec;
    for (func& f : func_vec)
        thread_vec.emplace_back(std::thread(f));
    for (thread& t : thread_vec)
        t.join();
    std::cout << "Atomic : " << atom;
}

可能有更好的方法来填充thread_vec(即迭代func_vec和thread_vec,将thread_vec的大小设置为func_vec’s),但这对于示例来说很好。如果您在主线程执行结束之前不需要线程正确结束,您可以完全放弃thread_vec,并创建一个您立即分离的线程,如另一个答案所示。