简体   繁体   中英

Pool of threads implementation using pthreads in C++

I'm having trouble in designing a program which has pool of threads.

The main issue i'm stuck at is when a thread is done with work the parent has to wait on the threadId (this is how a parent waits on thread using pthread_join). Because we do not know which of the threads is going to finish first, i'm not able figure out a programatic way to solve the problem.

Any explanation with a small snippet of code is appreciated.

Thanks.

Well, I don't know your setup or requirements, but you have run into the problem that pthread_join() waits for precisely one thread, and you actually want to wait for any thread.

Thus, the most obvious conclusion is that pthread_join doesn't help you. Sorry for stating the obvious, but I need to build up my case :-)

Instead, you'll probably have to come up with another idea. For example, you could wait on a condition variable; before a thread exits it would set the condition to "exited". The main thread could wait for that condition, iterate through the threads to find out which ones have terminated (it can be more than one), and eventually reset the condition. The condition mutex is usually sufficient to prevent races.

Threads could also add some ID into a list of exited threads in addition to setting the condition (you can use the condition mutex to protect that list), so the main thread only has to go through that list instead of checking every thread.

Pseudocode:

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"

Inside your threads:

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

Pools are usually implemented as a number of threads waiting on a producer-consumer queue for task items. The tasks are objects derived from a Task class that has a 'run()' method. Whichever thread gets a task, it calls run() and, when that returns, the thread loops round to get another task object from the queue.

This blows away any thread micro-management and works reliably and safely on every system/language I've ever tried it on.

The most flexible way I know for completion notification is for the thread to call an 'OnComplete' event, or possibly a virtual 'completed' method, of th task when run() returns, just before looping back to get the next task, with the task as a parameter. This method/event could, for example, signal an event/condvar/sema upon which the task-originating thread is waiting, could queue the completed task to the originating thread or another thread or even just delete() the task, (maybe it's job is completely done in the thread pool).

For error notification, I catch any uncaught exceptions thrown by run() and store the exception in a tast field before calling the completion method/event.

There are no locks except those protecting the producer-consumer queue, (and they are only taken for long enough to push/pop a *task).

Whichever design you use, please, please try very hard to not:

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

The ideal thread pool is where you have no idea which thread did the work. In fact, there is usually no need even to keep any reference to the threads. Two-line pseudo threadPool:

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

If you're trying to implement a thread pool, have a look at this architecture I played with a few years ago. I've written it up here: Thread Pooling in C++

For non-blocking pthread_join, see this SO discussion: Non-blocking pthread_join

If you're just waiting for all of the threads to finish their work, you can wait on them one-by-one: the order does not matter, and there is no reason to use conditions. This example demonstrates:

#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;
}

Although the order of the threads completes in the reverse order to the waiting (ie thread 0 finishes last, but we wait on thread 0 first), the main thread will not exit until all threads are finished. No conditions required.


The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM