here im trying to, use a circular queish thing for my insert and remove methods, as well as use the insert and remove functions for synchronization
im compiling with gcc file.c -lpthread -lrt and running with ./a.out 50 1 1.
upon running my producer produces and outputs correctly
producer thread #0
consumer thread #0
producer produced 7
but for whatever reason i cannot get my consumer to output,
i have tried putting my return 0; for each functions outside the locks and semaphores and it does indeed output consumer, but too many times when it should only run once per thread,
its also using a standard counting semaphore 2 of them as well as a single mutex lock
this is what happens when i move the return below the locks
producer thread #0
consumer thread #0
producer produced 7
consumer consumed 7
producer produced 5
producer produced 5
consumer consumed 5
consumer consumed 2
producer produced 7
producer produced 4
consumer consumed 7
consumer consumed 4
producer produced 6
consumer consumed 6
producer produced 1
consumer consumed 1
producer produced 9
consumer consumed 9
producer produced 2
consumer consumed 2
producer produced 6
consumer consumed 6
when in reality there should if i run ./a.out 50 1 1 there should only be one producer and one consumer
update: working on a new version on this so taking back my previous code, will post new version
In the functions insert_item() and remove_item(), returning (end of execution of the functions) happens before executing pthread_mutex_unlock(). This will prevent them from unlocking and prevent 2nd or later execution of the functions.
Instead of this, you should store the value to return in a variable and return that after unlocking. This can be done like this:
int insert_item(buffer_item item)
{
int ret;
sem_wait(&empty);
pthread_mutex_lock(&lock);
if (size_check < BUFFER_SIZE)
{
buffer[insertBounds_check++] = item;
size_check++;
ret = 0;
}
else
{
ret = -1;
}
pthread_mutex_unlock(&lock);
sem_post(&full);
return ret;
}
Related
I'm very new to thread coding, so I was doing an exercise I found out there on the web, which asks for something like this:
Write a program hellomany.c that will create a number N of threads specified in the command line, each of which prints out a hello message and its own thread ID. To see how the execution of the threads interleaves, make the main thread sleep for 1 second for every 4 or 5 threads it creates. The output of your code should be similar to:
I am thread 1. Created new thread (4) in iteration 0...
Hello from thread 4 - I was created in iteration 0
I am thread 1. Created new thread (6) in iteration 1...
I am thread 1. Created new thread (7) in iteration 2...
I am thread 1. Created new thread (8) in iteration 3...
I am thread 1. Created new thread (9) in iteration 4...
I am thread 1. Created new thread (10) in iteration 5...
Hello from thread 6 - I was created in iteration 1
Hello from thread 7 - I was created in iteration 2
Hello from thread 8 - I was created in iteration 3
Hello from thread 9 - I was created in iteration 4
Hello from thread 10 - I was created in iteration 5
I am thread 1. Created new thread (11) in iteration 6...
I am thread 1. Created new thread (12) in iteration 7...
Hello from thread 11 - I was created in iteration 6
Hello from thread 12 - I was created in iteration 7
What I've managed to code is this:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void* PrintHello(int iteration)
{
printf("Hello from thread %u - I was created in iteration %d \n",
pthread_self(), iteration);
pthread_exit(NULL);
}
int main(void)
{
int rc;
pthread_t thread_id;
int tidMain;
int n, i;
tidMain = pthread_self();
printf("How many threads are there to be created?\n");
scanf("%d", &n);
for(i = 1; i <= n; i++) {
pthread_t thread_id;
rc = pthread_create(&thread_id, NULL, PrintHello, i);
printf("I am thread %u. Created new thread (%u) in iteration %d\n",
tidMain, thread_id, i);
if(rc)
{
printf("\n ERROR: return code from pthread_create is %d \n", rc);
exit(1);
}
if((i % 5) == 0) {
sleep(1);
}
}
pthread_exit(NULL);
}
The program does really print what I want to, and even sleeps and "breaks" the creation each 5 threads created; still, I don't know why, but when each created thread gets to be executed (after main thread has informed they have been created) in the 5-thread-printing-streak i want to, first thread that "greets" is the last created one. What i get from the console, i.e. when I ask it to create 8 threads, is this:
I am thread 3075630848. Created new thread (3075627840) in iteration 1
I am thread 3075630848. Created new thread (3067235136) in iteration 2
I am thread 3075630848. Created new thread (3058842432) in iteration 3
I am thread 3075630848. Created new thread (3050449728) in iteration 4
I am thread 3075630848. Created new thread (3042057024) in iteration 5
Hello from thread 3042057024 - I was created in iteration 5
Hello from thread 3050449728 - I was created in iteration 4
Hello from thread 3058842432 - I was created in iteration 3
Hello from thread 3067235136 - I was created in iteration 2
Hello from thread 3075627840 - I was created in iteration 1
I am thread 3075630848. Created new thread (3032480576) in iteration 6
I am thread 3075630848. Created new thread (3024087872) in iteration 7
I am thread 3075630848. Created new thread (3015695168) in iteration 8
Hello from thread 3015695168 - I was created in iteration 8
Hello from thread 3024087872 - I was created in iteration 7
Hello from thread 3032480576 - I was created in iteration 6
As long as I understand, it is first executing the last thread created. Why does this happen? Can I get it to execute the first created one first?
BTW: I'm running on Ubuntu
This is the point about concurrent programming. You can never make any assumptions about the order in which the threads execute. I, for example get the following output:
I am thread 639280960. Created new thread (630781696) in iteration 1
Hello from thread 630781696 - I was created in iteration 1
I am thread 639280960. Created new thread (622388992) in iteration 2
Hello from thread 622388992 - I was created in iteration 2
I am thread 639280960. Created new thread (613996288) in iteration 3
Hello from thread 613996288 - I was created in iteration 3
I am thread 639280960. Created new thread (536868608) in iteration 4
Hello from thread 536868608 - I was created in iteration 4
I am thread 639280960. Created new thread (526280448) in iteration 5
Hello from thread 526280448 - I was created in iteration 5
I am thread 639280960. Created new thread (517887744) in iteration 6
I am thread 639280960. Created new thread (509495040) in iteration 7
Hello from thread 509495040 - I was created in iteration 7
I am thread 639280960. Created new thread (501102336) in iteration 8
Hello from thread 501102336 - I was created in iteration 8
Hello from thread 517887744 - I was created in iteration 6
If I run it again, I might get a different output. Try it out on your own!
The Wikipedia Article about Concurrent computing, says the following:
The exact timing of when tasks in a concurrent system are executed
depend on the scheduling, and tasks need not always be executed
concurrently. For example, given two tasks, T1 and T2:
T1 may be executed and finished before T2 or vice versa (serial and sequential)
T1 and T2 may be executed alternately (serial and concurrent)
T1 and T2 may be executed simultaneously at the same instant of time (parallel and concurrent)
I suggest, you read some basic articles or literature about concurrency and parallelism.
It's a concurrent program. If you run your program several times, the order may be different even in the same host.
By the way, maybe it is better for you to see pthread API first before coding.
For pthread_create():
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
The 3rd argument is void *(*start_routine)(void *) rather than void *(start_routine)(int), and the 4th argument is void * rather than int.
The order in which threads are executed is undetermined. If you need a specific order than you have to use locks, mutexes and conditions to order their executuion to your requirements.
The order that you see is caused by your hardware and kernel and sometimes randomness. I'm assuming you have a single core cpu or the output would make little sense. When you create a thread your kernel simply schedules the thread to run at a later time and keeps executing the main thread. So you get messages for all 5 thread creations. Only when the main thread sleeps the kernel switches to the next thread ready to run. It seems to do this as FILO (first in - last out). That just happens to be what your kernel in your version on your hardware happens to do. Could change at any moment.
pOwl on the other hand seems to have multiple cores. So while the main thread creates thread the other core(s) already execute them. Or he simply has a different kernel.
I'm reading stevens's book: apue. 2e. I have encountered a problem, about Mutex.
Let's see a peice of code first: (the following code comes from figure 11.10 of apue 2e)
#include <stdlib.h>
#include <pthread.h>
struct foo {
int f_count;
pthread_mutex_t f_lock;
/* ... more stuff here ... */
};
struct foo *
foo_alloc(void) /* allocate the object */
{
struct foo *fp;
if((fp = malloc(sizeof(struct foo))) != NULL){
fp->f_count = 1;
if(pthread_mutex_init(&fp->f_lock, NULL) != 0){
free(fp);
return NULL;
}
/* ... continue initialization ... */
}
return (fp);
}
void
foo_hold(struct foo *fp) /* add a reference to the object */
{
pthread_mutex_lock(&fp->f_lock);
fp->f_count++;
pthread_mutex_unlock(&fp->f_lock);
}
void
foo_rele(struct foo *fp) /* release a reference to the object */
{
pthread_mutex_lock(&fp->f_lock);
if(--fp->f_count == 0){ /* last reference */
pthread_mutex_unlock(&fp->f_lock); /* step 1 */
pthread_mutex_destroy(&fp->f_lock); /* step 2 */
free(fp);
}else{
pthread_mutex_unlock(&fp->f_lock);
}
}
Assuming we have two threads: thread1 and thread2. thread1 is running right now.
it calls function foo_rele and has finished the execution of step 1(see above),
and prepare to execute step2.
however, context switch occurs at this moment. and thread2 starts to execute.
thread2 locks the lock fp->f_lock then does something. but before thread2 unlocks the lock, context switch occurs again.
thread2 stops executing and thread1 starts to execute. thread1 destroy the lock and it's known to us all that error generates.
so, my problem is: Is the situation mentioned above possible to happen?
how can we avoid it and is there any interface(API) that can unlock and destroy a mutex atomically?
I agree with usr (+1), the design is wrong.
Generally speaking, before one thread can destroy anything it must establish that all other threads have finished using it. That requires some other form of inter-thread synchronisation. What you need is a way in which thread 1 can tell thread 2 that the resource needs to be freed, and for thread 2 to acknowledge that so that thread 1 can call foo_rele() knowing for sure that thread 2 will not try to use fp ever again. Or, for thread 2 telling thread 1 that it no longer needs fp and thread 1 calling foo_rele() as a consequence.
This can be accomplished several ways. One is for thread 1 to set a flag under the protection of the lock, and then wait for thread 2 to quit. Meanwhile, thread 2 eventually gets round to seeing the flag, and then quits. This releases thread 1 which then calls foo_rele(). Another way is message passing over pipes (my preferred means of inter-thread synchronisation); something along the lines of thread 1 -> thread 2, "Please stop using fp": thread 2 -> thread 1, "Ok" (though of course a better defined message set based on enums is advisable, but you get my meaning).
What you can't ever have is thread 1 calling foo_rele() without thread 2 either knowing that it must never touch fp ever again or thread 2 having already quit.
This is not well-designed because thread2 accesses an object which might have been destroyed already. Add a reference (increment f_count) before starting thread2. That way thread2 already starts out with the guarantee that the object is stable while it is running.
I'm using mutexes to try to restrict access to certain part of the codes to one thread, but instead of locking once and blocking the others, it seems to allow all threads to "lock". Following is my code, and then a section of the output showing that the code is not working.
//headers defined, etc
pthread_mutex_t queuemutex = PTHREAD_MUTEX_INITIALIZER;
// other code with various functions
int main(void) {
//unrelated code
threadinformation **threadArray = (threadinformation **)malloc( POOLSIZE * sizeof(threadinformation) );
int k;
for (k = 0; k < POOLSIZE; k++) {
pthread_t thread;
threadinformation *currentThread = (threadinformation *)malloc(sizeof(threadinformation));
currentThread->state = (int *)malloc(sizeof(int));
currentThread->state[0] = 0;
currentThread->currentWaiting = currentWaiting;
currentThread->number = k;
threadArray[k] = currentThread;
pthread_create(&thread, NULL, readWriteToClient, threadArray[k]); //thread is created here
currentThread->thread = thread;
joinArray[k] = thread;
}
//unrelated code
}
static void* readWriteToClient(void *inputcontent) {
while(1){
//unrelated code
pthread_mutex_lock(&queuemutex); //problem happens here
fprintf(stderr,"Thread %d got locked \n",threadInput->number);
while((threadInput->currentWaiting->status) == 0){
pthread_cond_wait(&cond, &queuemutex);
fprintf(stderr,"Thread %d got signalled \n",threadInput->number);
}
connfd = threadInput->currentWaiting->fd;
threadInput->currentWaiting->status = 0;
pthread_cond_signal(&conncond);
pthread_mutex_unlock(&queuemutex);
//unrelated code
}
}
Output.
Thread 0 got locked
Thread 7 got locked
Thread 25 got locked
Thread 97 got locked
Thread 6 got locked
Thread 5 got locked
Thread 4 got locked
Thread 3 got locked
Thread 8 got locked
Thread 9 got locked
Thread 10 got locked
Thread 11 got locked
Thread 12 got locked
Thread 13 got locked
Thread 14 got locked
Thread 15 got locked
Thread 16 got locked
Thread 17 got locked
Thread 18 got locked
Thread 19 got locked
Thread 20 got locked
And so on...
There is no problem.
pthread_cond_wait(&cond, &queuemutex);
waiting on a condition variable RELEASES the mutex.
The longer version of what fceller said is that, pthread_cond_wait(&cond, &mutex) does three things before it returns: It releases the lock, and then it waits for a signal, and then it waits (if necessary) to re-acquire the lock.
Your example does not show what (if anything) ever sends the first signal, and it does not show what (if anything) ever sets the status that the worker threads are waiting for.
Here is a more typical use-case for pthread_cond_wait() and pthread_cond_signal().
void producer() {
pthread_mutex_lock(&mutex);
push_something_on_the_queue();
ptherad_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
void consumer() {
pthread_mutex_lock(&mutex);
while (! time_to_quit) {
if (queue_is_empty) {
pthread_cond_wait(&cond, &mutex);
} else {
thing = take_something_from_the_queue();
pthread_mutex_unlock(&mutex);
do_something_with(thing);
pthread_mutex_lock(&mutex);
}
}
pthread_mutex_unlock(&mutex);
}
A producer thread puts things in a queue. A consumer thread waits for things to appear in the queue, and then it pops them off and does something with them. The consumer waits in a loop, and it checks the status of the queue each time it wakes up (A thread may be "falsely" signalled even when there is nothing in the queue).
The mutex is locked when the consumer is checking the queue, and it remains locked until the consumer either sleeps or pops a thing off the queue. The mutex is not locked while the consumer is sleeping, and it is not locked while the consumer is doing whatever it does with a thing.
Any number of consumers can be in the consumer() method at the same time. They may be operating on different things, or they may be sleeping; but the mutex is insures that no more than one thread (producer or consumer) can touch the queue at any given time.
I have assignment to work on producer and consumer problem by use thread and semaphore. The task is that allow user to define # of producer,# of consumer and buffer size. The program always lock if producer reach the buffersize. But the requirement says if producer reach buffersiz the consumer thread should start and take things from buffer. I am out of idea how to fix this problem and my teacher refuse to help. I am a totally beginner of the C language, please give me some suggestion. Thank you very much
My program can run when Producer = Consumer, or Producer < Consumer, except Producer > Buffer Size, it seems to appear deadlock, and I think I understand the reason why but I don't know how to fix the code to let Consumer thread run first than back to Producer thread.
Here is the running result when producer =3 consumer = 1 and buffersize = 2
./Task2 3 1 2
Producer 0 has started
Producer 0:Put item 0.
Producer 1 has started
Producer 1:Put item 1.
Producer 2 has started
The requirement says the result should looks like
Started
Producer 0 has started
Producer 0: Put item 0.
Producer 1 has started
Producer 1: Put item 1.
Producer 2 has started
Consumer 0 has started
Consumer 0: Taked item 0.
Producer 2: Put item 2.
Terminated!
Here is my origional code, I have discard some input error check code
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
pthread_t *pid, *cid;
void *producer(void *param);
void *consumer(void *param);
void init();
int Remove();
struct prot_buffer{
int Producer;
int Consumer;
int *buffer;
int buffersize;
int front;
int rear;
int item;
sem_t mutex;
sem_t slots;
sem_t items;
}b;
main(int argc, char *argv[]){
int c1;
b.Producer = atoi(argv[1]);
b.Consumer = atoi(argv[2]);
b.buffersize = atoi(argv[3]);
init();
pid = (pthread_t *)malloc(b.Producer *sizeof(pthread_t));
cid = (pthread_t *)malloc(b.Consumer *sizeof(pthread_t));
for (c1=0; c1< b.Producer; c1++){
printf("Producer %d has started\n", c1);
pthread_create(&(pid[c1]),NULL, producer, NULL);
pthread_join(pid[c1], NULL);
printf("Producer %d:Create item %d.\n", c1,c1);
}
/* Create the consumer threads */
for (c1=0; c1<b.Consumer; c1++){
printf("Consumer %d has started\n", c1);
pthread_create(&(cid[c1]),NULL, consumer, NULL);
if (b.front==b.rear){
printf("Terminated!\n");
exit(0);
}
pthread_join(cid[c1], NULL);
printf("Consumer %d:Taked item %d.\n", c1, c1);
}
free(b.buffer);
free(pid);
free(cid);
sem_destroy(&b.items);
sem_destroy(&b.slots);
sem_destroy(&b.mutex);
printf("Threads terminated!\n");
exit(0);
}
void *producer(void *param){
sem_wait(&b.slots); sem_wait(&b.mutex);
if(b.rear<=b.buffersize){
b.buffer[b.rear] = b.item;
b.rear++;
sem_post(&b.mutex); sem_post(&b.items);
}else{
sem_post(&b.mutex); sem_post(&b.items); }
}
void *consumer(void *param){
Remove();
}
void init(){
b.buffer = (int *) malloc(b.buffersize *sizeof(int));
b.buffersize = b.buffersize;
b.front = b.rear =0;
sem_init(&b.items, 0, 0);
sem_init(&b.slots,0,b.buffersize);
sem_init(&b.mutex, 0, 1);
}
int Remove(){
sem_wait(&b.items);
sem_wait(&b.mutex);
b.item = b.buffer[b.front];
b.front++;
sem_post(&b.mutex);
sem_post(&b.slots);
return b.item;
}
My new code
main(int argc, char *argv[]){
...
pthread_create(&pid,NULL, producer, NULL);
pthread_create(&cid,NULL, consumer, NULL);
....
}
void *producer(void *param){
int c2;
for (c2=0; c2 < b.Producer; c2++) {
printf("Producer %d has started\n", c2);
b.item = c2;
sem_wait(&b.slots);
sem_wait(&b.mutex);
b.buffer[b.rear] = b.item;
b.rear = (b.rear+1)%b.buffersize;
printf("Producer %d:Put item %d.\n", c2,c2);
sem_post(&b.mutex);
sem_post(&b.items);
}
return NULL;
}
void *consumer(void *param){
int c2;
for (c2=0; c2 < b.Consumer; c2++) {
printf("Consumer %d has started\n", c2,c2);
b.item = c2;
sem_wait(&b.items);
sem_wait(&b.mutex);
b.buffer[b.front] = b.item;
b.front = (b.front+1)%b.buffersize;
printf("Consumer %d:take item %d.\n", c2, c2);
sem_post(&b.mutex);
sem_post(&b.slots);
}
return NULL;
}
To avoid trobule at school I remove some code and some description.
The program result is correct now, thanks for help. In this case I use b.item as the variable to display the item left inside the buffer, but its wrong. Use other variable like front or rear also not work too.
Program result-
Producer=2, Consumer=2, Buffer=2
./F 2 2 2
started
Producer 0 has started
Producer 0:Put item 0.
Producer 1 has started
Producer 1:Put item 1.
Consumer 0 has started
Consumer 0:Take item 0.
Consumer 1 has started
Consumer 1:Take item 1.
1 item(s) left in the buffer! //This is wrong!
Terminated!
Producer=3, Consumer=1, Buffer=2
./F 3 1 2
started
Producer 0 has started
Producer 0:Deposited item 0.
Producer 1 has started
Producer 1:Deposited item 1.
Producer 2 has started
Consumer 0 has started
Consumer 0:Removed item 0.
Producer 2:Deposited item 2.
0 item(s) left in the buffer! //Still wrong!
Terminated!
Producer =2, Consumer = 5, Buffer =3
./F 2 5 3
started
Producer 0 has started
Producer 0:Put item 0.
Producer 1 has started
Producer 1:Put item 1.
Consumer 0 has started
Consumer 0:Take item 0.
Consumer 1 has started
Consumer 1:Take item 1.
Consumer 2 has started
2 item(s) left in the buffer! //Wrong again!
Terminated!
Your buffer size is 2. The first 2 producers fill up this buffer. Hence the third one is waiting for a consumer to take one item so that it can add to buffer. But your pthread_join inside the producer loop never allows for consumers to be created at all! pthread_join is suspending main process until 3rd producer terminates. Hence the deadlock where in the third Producer is indefinitely waiting for the buffer to be freed by a consumer who never arrives.
I suggest you go through Exercise 3 of this article which deals with exactly the same problem and the same data structure as yours. They've clearly articulated how to prevent buffer overflow, where to print producer data. May be its a standard semaphore tutorial exercise for grads.
Your initialization and use of the semaphores seems correct - they are a natural choice for producer-consumer queues - it's condvars that are not, (condvars have no count and so require while loops for correct operation even if your OS does not support the spurious wakeup feature).
Your use of the buffer, however, seems a bit 'off'. I presume that the buffer is supposed to be circular? If so, you should be setting the index back to 0 whenever the front, or rear, index reaches the end of the array. You should be doing this inside the mutex lock in both the producer and consumer code. The check on the indexes should be solely to reset them to the start of the buffer and should not change the operation of the semaphore signal/wait in any way.
That, and you seem to be joining the threads as soon as they are created. Usually, to test/demonstrate producer/consumer operation, threads are not continually created, immediately joined, (so that the constructing thread has to immediately wait), and then left to terminate. It's more common to run up some producer and consumer threads that loop, continually producing/consuming messages, (perhaps with a very short sleep loop - those kind of loops that some developers say have no valid use and are an anti-pattern).
Continually creating/terminating/destroying threads is very wasteful and will result, in your test code, in an app that is performing nearly all overhead and very little producer/consumer.
Last thing - don't join to threads without thinking about it for a second or two first. It may not be optimal, it may not be necessary, it may be a hindrance, it may be a disaster. If the join is solely to keep your main thread, (and so process), from terminating early, it may be better to find another way - wait on keyboard input or use a long-term sleep() loop.
My question is similar to How do I check if a thread is terminated when using pthread?. but i did not quite get an answer.
My problem is...I create a certain number of threads say n. As soon as main detects the exit of any one thread it creates another thread thus keeping the degree of concurrency as n and so on.
How does the main thread detect the exit of a thread. pthread_join waits for a particular thread to exit but in my case it can be any one of the n threads.
Thanks
Most obvious, without restructuring your code as aix suggests, is to have each thread set something to indicate that it has finished (probably a value in an array shared between all threads, one slot per worker thread), and then signal a condition variable. Main thread waits on the condition variable and each time it wakes up, handle all threads that have indicated themselves finished: there may be more than one.
Of course that means that if the thread is cancelled you never get signalled, so use a cancellation handler or don't cancel the thread.
There are several ways to solve this.
One natural way is to have a thread pool of fixed size n and have a queue into which the main thread would place tasks and from which the workers would pick up tasks and process them. This will maintain a constant degree of concurrency.
An alternative is to have a semaphore with the initial value set to n. Every time a worker thread is created, the value of the semaphore would need to be decremented. Whenever a worker is about to terminate, it would need to increment ("post") the semaphore. Now, waiting on the semaphore in the main thread will block until there's fewer than n workers left; a new worker thread would then be spawned and the wait resumed. Since you won't be using pthread_join on the workers, they should be detached (pthread_detach).
If you want to be informed of a thread exiting (via pthread_exit or cancellation), you can use a handler with pthread_cleanup_push to inform the main thread of the child exiting (via a condition variable, semaphore or similar) so it can either wait on it, or simply start a new one (assuming the child is detached first).
Alternately, I'd suggest having the threads wait for more work (as suggested by #aix), rather than ending.
If your parent thread needs to do other other things, then it can't just constantly be blocking on pthread_join, You will need a way to send a message to the main thread from the child thread to tell it to call pthread_join. There are a number of IPC mechanisms that you could use for this.
When a child thread has done it's work, it would then send some sort of message to the main thread through IPC saying "I completed my job" and also pass its own thread id, then the main thread knows to call pthread_join on that thread id.
One easy way is to use a pipe as a communication channel between the (worker) threads and your main thread. When a thread terminates it writes its result (thread id in the following example) to the pipe. The main thread waits on the pipe and reads the thread result from it as soon as it becomes available.
Unlike mutex or semaphore, a pipe file descriptor can be easily handled by the application main event loop (such as libevent). The writes from different threads to the same pipe are atomic as long as they write PIPE_BUF or less bytes (4096 on my Linux).
Below is a demo that creates ten threads each of which has a different life span. Then the main thread waits for any thread to terminate and prints its thread id. It terminates when all ten threads have completed.
$ cat test.cc
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
void* thread_fun(void* arg) {
// do something
unsigned delay = rand() % 10;
usleep(delay * 1000000);
// notify termination
int* thread_completed_fd = static_cast<int*>(arg);
pthread_t thread_id = pthread_self();
if(sizeof thread_id != write(*thread_completed_fd, &thread_id, sizeof thread_id))
abort();
return 0;
}
int main() {
int fd[2];
if(pipe(fd))
abort();
enum { THREADS = 10 };
time_t start = time(NULL);
// start threads
for(int n = THREADS; n--;) {
pthread_t thread_id;
if(pthread_create(&thread_id, NULL, thread_fun, fd + 1))
abort();
std::cout << time(NULL) - start << " sec: started thread " << thread_id << '\n';
}
// wait for the threads to finish
for(int n = THREADS; n--;) {
pthread_t thread_id;
if(sizeof thread_id != read(fd[0], &thread_id, sizeof thread_id))
abort();
if(pthread_join(thread_id, NULL)) // detached threads don't need this call
abort();
std::cout << time(NULL) - start << " sec: thread " << thread_id << " has completed\n";
}
close(fd[0]);
close(fd[1]);
}
$ g++ -o test -pthread -Wall -Wextra -march=native test.cc
$ ./test
0 sec: started thread 140672287479552
0 sec: started thread 140672278759168
0 sec: started thread 140672270038784
0 sec: started thread 140672261318400
0 sec: started thread 140672252598016
0 sec: started thread 140672243877632
0 sec: started thread 140672235157248
0 sec: started thread 140672226436864
0 sec: started thread 140672217716480
0 sec: started thread 140672208996096
1 sec: thread 140672208996096 has completed
2 sec: thread 140672226436864 has completed
3 sec: thread 140672287479552 has completed
3 sec: thread 140672243877632 has completed
5 sec: thread 140672252598016 has completed
5 sec: thread 140672261318400 has completed
6 sec: thread 140672278759168 has completed
6 sec: thread 140672235157248 has completed
7 sec: thread 140672270038784 has completed
9 sec: thread 140672217716480 has completed