我有一个程序需要每次迭代运行一次M次函数,并且这些运行可以并行化。可以说我一次只能运行N个线程(比如可用的核心数)。我需要一个算法来确保我总是运行N个线程(只要剩余的线程数是> = N)并且该算法需要对这些线程的完成顺序不变。此外,线程调度算法不应占用大量CPU时间。
我有类似以下内容的东西,但它显然存在缺陷。
#include <iostream>
#include <pthread.h>
#include <cstdlib>
void *find_num(void* arg)
{
double num = rand();
for(double q=0; 1; q++)
if(num == q)
{
std::cout << "\n--";
return 0;
}
}
int main ()
{
srand(0);
const int N = 2;
pthread_t threads [N];
for(int q=0; q<N; q++)
pthread_create(&threads [q], NULL, find_num, NULL);
int M = 30;
int launched=N;
int finnished=0;
while(1)
{
for(int w=0; w<N; w++)
{
//inefficient if `threads [1]` done before `threads [2]`
pthread_join( threads [w], NULL);
finnished++;
std::cout << "\n" << finnished;
if(finnished == M)
break;
if(launched < M)
{
pthread_create(&threads [w], NULL, find_num, NULL);
launched++;
}
}
if(finnished == M)
break;
}
}
这里显而易见的问题是,如果threads[1]
在threads[0]
之前完成,则会浪费CPU时间,而我无法想到如何解决这个问题。另外,我假设让pthread_join()
上的主程序等待CPU时间没有显着消耗?
答案 0 :(得分:5)
我建议反对重新生成线程,这是一个相当严重的开销。相反,创建一个由N个线程组成的池,并通过工作队列(一种相当标准的方法)向它们提交工作。即使你的剩余工作量小于N,额外的线程也不会造成任何伤害,它们只会在工作队列中被阻塞。
如果您坚持现有方法,可以这样做:
不要等待pthread_join
的线程,你不需要它,因为你没有将任何东西传回主线程。创建具有属性PTHREAD_CREATE_DETACHED
的线程,然后让它们退出。
在主线程中,等待每个退出线程发出信号的信号量 - 实际上你会等待任何线程终止。如果由于任何原因没有<semaphore.h>
,使用互斥锁和条件实现它是微不足道的。
#include <semaphore.h>
#include <iostream>
#include <pthread.h>
#include <cstdlib>
sem_t exit_sem;
void *find_num(void* arg)
{
double num = rand();
for(double q=0; 1; q++)
if(num == q)
{
std::cout << "\n--";
return 0;
}
/* Tell the main thread we have exited. */
sem_post (&exit_sem);
return NULL;
}
int main ()
{
srand(0);
/* Initialize pocess private semaphore with 0 initial count. */
sem_init (&exit_sem, 0, 0);
const int N = 2;
pthread_attr_t attr;
pthread_attr_init (&attr);
pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
for(int q=0; q<N; q++)
pthread_create(NULL, &attr, find_num, NULL);
int M = 30;
int launched=N;
int finnished=0;
while(1)
{
for(int w=0; w<N; w++)
{
/* Wait for any thread to exit, don't care which. */
sem_wait (&exit_sem);
finnished++;
std::cout << "\n" << finnished;
if(finnished == M)
break;
if(launched < M)
{
pthread_create(NULL, &attr, find_num, NULL);
launched++;
}
}
if(finnished == M)
break;
}
}
无论如何,我会再次推荐线程池/工作队列方法。
答案 1 :(得分:1)
如果main()在pthread_join上等待(假设你的平台上没有实现它只是一个自旋锁),它将不会导致CPU负载;如果pthread_join正在等待互斥锁,则调度程序将不会在该互斥锁发出信号之前随时提供该线程。
如果N确实是核心数,那么你可能会忘记自己管理线程调度;操作系统调度程序将负责这一点。如果N小于一个或多个核心,也许你可以设置线程亲和力来仅在N个核心上运行你的进程(或者如果你不想为你的进程的其余部分设置线程亲和性,那么会产生一个计算过程) );同样,这样做的目的是让操作系统调度程序处理调度。
答案 2 :(得分:1)
我绝对会看到OpenMP或C ++ 11 async
。说实话,在这一点上我认为OpenMP更可行。
这是一个快速示例,有时会使用2个线程随机找到正确的答案(42
)。
请注意,如果省略omp.h
include并调用omp_set_num_threads(2);
,您将获得本机线程数(即取决于运行时可用的核心数)。 OpenMP还允许您通过设置环境变量来动态配置此数字。 OMP_NUM_THREADS=16
。实际上,您可以动态地禁用并行性:)
我甚至投入了一个示例线程参数和结果累积 - 这通常是事情变得更有趣然后开始工作而忘记它。再说一遍,你的问题可能有点过分:)
使用g++ -fopenmp test.cpp -o test
#include <iostream>
#include <cstdlib>
#include <omp.h>
int find_num(int w)
{
return rand() % 100;
}
int main ()
{
srand(time(0));
omp_set_num_threads(2); // optional! leave it out to get native number of threads
bool found = false;
#pragma omp parallel for reduction (|:found)
for(int w=0; w<30; w++)
{
if (!found)
{
found = (find_num(w) == 42);
}
}
std::cout << "number was found: " << (found? "yes":"no") << std::endl;
}
答案 3 :(得分:1)
简单的解决方案是在线程完成时设置一个全局变量,并且主循环轮询该变量以检查轮胎何时完成,然后pthread_join
。