【问题标题】:Pool of threads implementation using pthreads in C++在 C++ 中使用 pthreads 实现线程池
【发布时间】:2012-07-05 17:45:27
【问题描述】:

我在设计具有线程池的程序时遇到了麻烦。

我遇到的主要问题是当一个线程完成工作时,父级必须等待 threadId(这是父级使用 pthread_join 等待线程的方式)。因为我们不知道哪个线程会先完成,所以我无法找到解决问题的编程方法。

感谢任何带有小sn-p代码的解释。

谢谢。

【问题讨论】:

  • 您是在实现一个线程池,还是多个线程,每个线程都需要完成某个问题的某个部分?如果是后者,那么哪个先完成并不重要:您可以依次等待每个线程,因为即使第一个线程最后完成,其他线程也会在您等待它们时完成。

标签: c++ pthreads threadpool


【解决方案1】:

如果您尝试实现线程池,请查看我几年前使用过的这个架构。我已经写在这里了:Thread Pooling in C++

对于非阻塞 pthread_join,请参阅此 SO 讨论:Non-blocking pthread_join

如果你只是在等待所有线程完成它们的工作,你可以一个一个地等待它们:顺序无关紧要,没有理由使用条件。这个例子演示了:

#include <memory.h>
#include <pthread.h>

#include <iostream>

using namespace std;

#define NTHREADS 10

void *thread(void *arg) {
    int *n = (int *) arg;
    sleep(10 - *n);
    cout << "Thread " << (*n) << endl;
    delete n;
    return NULL;
}

int main(int argc, char **argv) {
    pthread_t threads[NTHREADS];
    pthread_attr_t attr;
    memset(&attr, 0, sizeof(attr));
    int i;
    for (i=0; i<NTHREADS; i++) {
        int *p = new int;
        *p = i;
        pthread_create(threads + i, &attr, thread, p);
    }
    void *rval;
    for (i=0; i<NTHREADS; i++) {
        pthread_join(threads[i], &rval);
        cout << "Joined thread " << i << endl;
    }
    return 0;
}

虽然线程的完成顺序与等待的顺序相反(即线程 0 最后完成,但我们先等待线程 0),但在所有线程完成之前,主线程不会退出。不需要条件。


【讨论】:

    【解决方案2】:

    池通常被实现为在生产者-消费者队列中等待任务项的多个线程。任务是派生自具有“run()”方法的任务类的对象。无论哪个线程获得一个任务,它都会调用 run(),当它返回时,线程循环以从队列中获取另一个任务对象。

    这消除了任何线程微管理,并且可以在我尝试过的每种系统/语言上可靠安全地工作。

    我所知道的完成通知最灵活的方式是线程在 run() 返回时调用任务的“OnComplete”事件,或者可能是虚拟的“完成”方法,就在循环返回获取下一个任务之前任务,以任务为参数。例如,此方法/事件可以发出一个事件/condvar/sema 信号,任务发起线程正在等待该事件/condvar/sema,可以将已完成的任务排队到发起线程或另一个线程,甚至只是 delete() 任务,(也许它是作业完全在线程池中完成)。

    对于错误通知,我会在调用完成方法/事件之前捕获任何由 run() 引发的未捕获异常并将异常存储在 tast 字段中。

    除了保护生产者-消费者队列的锁之外,没有其他锁(而且它们只占用足够长的时间来推送/弹出*任务)。

    无论您使用哪种设计,请尽量不要:

    1) continually create/terminate/destroy threads - avoidable overhead and tricky to manage
    2) wait with Join() for any thread to terminate - just don't :)
    3) loop around some 'poll thread status' to see if they're finished yet - gets it wrong
    4) Move 'working/finished' threads into and out of containers with complicated locks - deadlock-in-the-making
    5) use any other sort of micro-management - difficult, messy, error-prone, too many locks, unnecesary, avoidable
    

    理想的线程池是您不知道哪个线程完成工作的地方。事实上,通常甚至不需要保留对线程的任何引用。两行伪线程池:

    TblockingQueue *inQueue=new TblockingQueue();
    for(int i=0;i<CpoolDepth,i++) new Thread(inQueue);
    

    【讨论】:

      【解决方案3】:

      好吧,我不知道您的设置或要求,但您遇到了 pthread_join() 只等待一个线程的问题,而您实际上想要等待任何线程。

      因此,最明显的结论是 pthread_join 对您没有帮助。很抱歉说的很明显,但我需要建立我的案例:-)

      相反,您可能不得不想出另一个主意。例如,您可以等待条件变量;在线程退出之前,它会将条件设置为“退出”。主线程可以等待该条件,遍历线程以找出哪些线程已终止(可能不止一个),并最终重置条件。条件互斥锁通常足以防止竞争。

      除了设置条件之外,线程还可以将一些 ID 添加到已退出线程的列表中(您可以使用条件互斥锁来保护该列表),因此主线程只需通过该列表而不是检查每个线程.

      伪代码:

      initialize condition variable with status "not exited"
      ...
      ...
      launch your threads
      ...
      ...
      while (some threads are still running) do
         lock condition variable on "exited"
         iterate through threads, remove the ones that have exited
         unlock condition variable with new condition "not exited"
      

      在你的线程中:

       ...
       do whatever it needs to do
       ...
       ...
       lock condition variable
       unlock condition variable with new condition "exited"
       /* end of thread */
      

      【讨论】:

      • +1 只是为了 dissing join(),但也许我应该再次取消它以获得包含“遍历线程以找出哪些线程已终止”的答案:)
      猜你喜欢
      • 2014-04-12
      • 2012-06-16
      • 1970-01-01
      • 2011-04-03
      • 1970-01-01
      • 2011-09-11
      • 2018-09-23
      • 2011-10-20
      • 2012-05-14
      相关资源
      最近更新 更多