C ++ Pthreads:当>每次迭代必须运行N个线程时,同时运行N个线程的算法

时间:2011-11-01 07:55:46

标签: c++ multithreading algorithm pthreads

我有一个程序需要每次迭代运行一次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时间没有显着消耗?

4 个答案:

答案 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更可行。

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