Click to See Complete Forum and Search --> : waiting on pthread to finish


kenrus
August 1st, 2007, 04:13 PM
I am looking for the best (or just 'a better') way to have main() wait for all the threads to finish.

Right now, all of the threads have access to a structure that contains a count. Each thread increments this count by 1 when it finishes. main() continually checks this count and when it determines all of the threads have finished, it performs cleanup and exits.



#include <pthread.h>
#include <stdio.h>

typedef struct TagThreadData
{
pthread_mutex_t* pMutex;
int* pcthread;
char id[2];
}TThreadData;

void* mutex_test(void*);

int main(int argc, char** argv)
{
int rv_init = 0;
int rv_destroy = 0;
int rv_thread = 0;

int i = 0;

pthread_mutex_t mutex;
int cthread = 0;

TThreadData ThreadData[] = { {&mutex, &cthread, "A"},
{&mutex, &cthread, "B"},
{&mutex, &cthread, "C"},
{&mutex, &cthread, "D"},
{&mutex, &cthread, "E"},
{&mutex, &cthread, "F"},
{&mutex, &cthread, "G"},
{&mutex, &cthread, "H"},
{&mutex, &cthread, "I"},
{ 0, 0, 0}
};

pthread_t threads[10];

//INITIALIZE MUTEX
rv_init = pthread_mutex_init(&mutex, 0);

printf("\n[MAIN]: Mutex Initialization Status: %i", rv_init);

//LAUNCHING THREADS
for(i = 0; ThreadData[i].pMutex != 0; i++)
{
rv_thread = pthread_create(&threads[i], 0, mutex_test, (void*)(&ThreadData[i]));
}

while(cthread < i)
{
time_t start = 0;
time_t now = 0;

time(&start);

while((start + 1) > now)
time(&now);

pthread_mutex_lock(&mutex);
printf("\n[MAIN]: Waiting on threads to finish [%i]", cthread);
pthread_mutex_unlock(&mutex);
}

//DESTROY MUTEX
rv_destroy = pthread_mutex_destroy(&mutex);

printf("\n[MAIN]: Mutex Destruction Status: %i", rv_destroy);

return 0;
}

void* mutex_test(void* pszThreadId)
{
TThreadData* pThreadData = (TThreadData*) pszThreadId;

char* pszId = pThreadData->id;

pthread_mutex_t* pmutex = pThreadData->pMutex;

unsigned int i = 0;
int rv_lock = 0;
int rv_unlock = 0;
time_t start_t = 0;
time_t now_t = 0;

printf("\n[%s]: Starting...", pszId);


for(i = 0; i < 100; i++)
{


time(&start_t);

while((start_t + 1) > now_t)
time(&now_t);
printf("\n[%s]: Attempting to lock mutex...", pszId);

rv_lock = pthread_mutex_lock(pmutex);

printf("\n[%s]: mutex locked - rv_lock == %i", pszId, rv_lock);

time(&start_t);

while((start_t + 1) > now_t)
time(&now_t);

printf("\n[%s]: unlocking the mutex now", pszId);

rv_unlock = pthread_mutex_unlock(pmutex);

printf("\n[%s]: mutex has been unlocked - rv_unlock == %i", pszId, rv_unlock);
}

*(pThreadData->pcthread) += 1;
printf("\n[%s]: Finished - [%i]", pszId, *pThreadData->pcthread);
}





Is there a better way ?


Thanks for any help.

JVene
August 1st, 2007, 07:03 PM
I found this situation comes up as a common consequence of parallel algorithms.

What you want is an auto reset event. The calling task (pending thread completion) will wait on the event(s) until they're signaled.

The wait function has a timeout, so it's possible that the wait will release before it's really signaled. As such, it's common to use a state value (a bool) to indicate if the task completion is the reason the wait released or not. Using an int, it's possible to wait for multiple tasks to complete using a single auto reset event, though you could make use of a semaphore for this purpose (I've seen it used that way, but I'm not fond of the approach).

Like locks, it's a good idea to wrap the concept of waiting into an object, so you're not coding and re-coding this rather common pattern.

exterminator
August 22nd, 2007, 01:20 PM
pthread_join probably exists for a reason?

JVene
August 22nd, 2007, 06:00 PM
pthread_join probably exists for a reason?


I personally choose not to use it because it's not portable to Windows, whereas event methods are, but when portability isn't a concern 'join' makes sense.

exterminator
August 22nd, 2007, 10:57 PM
I personally choose not to use it because it's not portable to Windows, whereas event methods are, but when portability isn't a concern 'join' makes sense.I thought it was. Can you point me to the information? I am referring to pthreads-win32 (http://sourceware.org/pthreads-win32/conformance.html) and it seems to be supporting this function. Do you mean it does not work or something else?

JVene
August 23rd, 2007, 02:50 PM
Yes, but that's not win32 - it's an open source component you can choose to add for use in win32.

Not that I'm 'knocking' that - there's certainly no reason it wouldn't work, and that's a viable route to choose for portability.

In my view, though, there are a number of POSIX/Linux/Windows portability concerns (like critical sections vs mutexes) that I feel are better served with C++ library adaptation rather than a plug in. In particular, since critical sections are lighter and faster than mutexes, Windows development benefits from using them. Object techniques can allow one body of application code to work on both platforms even though the underlying methods differ, so one can take advantage of these differences, though I'm aware there's plenty of debate for either choice.

exterminator
August 24th, 2007, 01:00 AM
Yes, portability is not an issue in this particular case. But it is better achieved by abstraction libraries like boost::threads.

By the way, how would implement an auto reset event in a portable way?

JVene
August 25th, 2007, 01:35 PM
But it is better achieved by abstraction libraries like boost::threads.


Exactly, which is what I was suggesting with:


....better served with C++ library adaptation rather than a plug in.


Albeit not quite so clearly.

Years ago I began committing my own solutions to threading to a library which I've carried forward throughout my work, bringing it to a functional state long before boost, the STL or even a popular version of Linux existed.

I had to stop and think, though, to reply to your question regarding an auto reset event in Linux. I don't even recall when I added the Linux adaptation to my AutoResentEvent and EventBase objects, which may have been plucked from the AIX/Unix version I wrote around '91/'92.

The object's interface is simple enough, and since I've only considered it's use for many years I simply forget the underlying details unless there's a problem that requires a fresh look.

For the Linux version, the underlying implementation uses a mutex and a pthread_cond_t member upon which I call pthread_cond_timedwait, and an integer to count the signal transitions.

From an application code viewpoint, whether in Windows, Linux, (OS/2 in the old days), MAC, etc...I need only create an AutoResetEvent instance, and at some point call Wait on that instance to block a thread waiting for a signal. Another thread calls Signal on the AutoResentEvent instance, and the waiting thread releases (or first in queue as it may happen).

It's true that the Windows version is short code, since the OS has such an event, but the concept functions the same in all implementations.

Your question did remind me that I should take more care when answering questions on Linux/Unix development issues, because my own use of libraries can cause me to forget the implementation differences.

exterminator
August 26th, 2007, 07:19 AM
Your question did remind me that I should take more care when answering questions on Linux/Unix development issues, because my own use of libraries can cause me to forget the implementation differences.My intent was to learn about your implementation because from the few implementations that I saw for windows they use WaitForSingleObject on the Event (auto reset) object handle which is analogous the linux/unix pthread_join atleast from functionality point of view. This can be guarded with condition compilation directives to make it portable considering the pthread_join port for windows does not work. I wished to know how you implemented it in your library. By the way, I could not find any documentation about a pthread port for windows that said that the pthread_join did not work. Just wanted a tick from the port documentation or something like that.

Anyways, as I see it, it would be a bad idea to choose not use a already in practice library for portable development unless that is what the developer wants to build for himself.

Hermit
August 26th, 2007, 09:28 PM
Yes, portability is not an issue in this particular case. But it is better achieved by abstraction libraries like boost::threads.
Speaking of which, using that library provides a very simple answer to the original post.

#include <boost/thread.hpp>

void thread1_func(void)
{
// do something
}

void thread2_func(void)
{
// do something else
}

int main(void)
{
boost::thread_group my_threads;
my_threads.create_thread(&thread1_func);
my_threads.create_thread(&thread2_func);

my_threads.join_all();

return 0;
}
Seems you're looking for a C solution though. I highly doubt there's any reason why pthread_join would be unsupported in a Windows port of pthread, so I'd say use that... that, or I'd say you're a masochist for choosing to write threaded code in C.

JVene
August 27th, 2007, 03:53 PM
that, or I'd say you're a masochist for choosing to write threaded code in C.


I quite well agree.


I could not find any documentation about a pthread port for windows that said that the pthread_join did not work


..and I hope I had not given the impression that I thought it wouldn't, only that I prefer object libraries to aid in adaptation for portability, and more generally for solutions. The support for pthread in win32 is a C level solution, and while I have little doubt it would work, I would already seek object level solutions to other problems anyway (events, critical sections, mutexes) and would continue the preference so as to obviate the need for the pthread support in win32.

I conceded, though, that for a C level solution it's hard to supplant pthread (and therefore joinable threads) with C approaches to waiting on a condition vs waiting on events in the interest of portability, and my own view is that portability is an important assumption while maintaining, where possible, native operation.