Solved

How to wait for windows c++  threads to terminate?

Posted on 2008-06-11
7
3,124 Views
Last Modified: 2008-06-13
Hi,

I am running into problems regarding threading during termination of a c++ console app.


In my main(), I create an object that, itself, launches many threads. The object, called "commGuy" is of type "MultThreadedCommunicator". When I eventually delete this object, some of the threads that it has launched have not yet completed. The result is a mess.  What I need is a way to wait until all of its threads terminate before I call delete.

The following code illustrates my problem:

Please do not suggest a solution that involves instancing a MultiThreadedCommunicator object directly. For my purposes, it must be referenced only via a pointer obtained by "new".

Thanks!

Kevin
int main()

{
 

	// instance commGuy

	MultiThreadedCommunicator* commGuy = new MultiThreadedCommunicator;
 

	// do a bunch of stuf

	.

	.

	.
 

	// it is now time to terminate the program
 

	// need some code here that waits until commGuy's threads

	// terminate before further program execution occurs
 
 

	// delete commGuy

	delete commGuy;
 

}

Open in new window

0
Comment
Question by:highqllc
  • 4
  • 2
7 Comments
 
LVL 40

Accepted Solution

by:
evilrix earned 250 total points
Comment Utility
You'll need to use a x-thread communication mechanism, such as an event, to signal the thread(s), you then use WaitForSingleObject or WaitForMultipleObjects to wait for them to finish.

http://msdn.microsoft.com/en-us/library/aa915075.aspx
http://msdn.microsoft.com/en-us/library/ms687032.aspx
http://msdn.microsoft.com/en-us/library/ms687025.aspx
0
 

Author Comment

by:highqllc
Comment Utility
evilrix,

I forgot to mention that I create threads with _beginthreadex().  The solution you provided seems to be for CreateThread().  Will it also work for _beginthreadex()?
0
 
LVL 40

Expert Comment

by:evilrix
Comment Utility
>> I forgot to mention that I create threads with _beginthreadex().  The solution you provided seems to be for CreateThread().  Will it also work for _beginthreadex()?

Hmmm... the problem is you need to wait of the thread handle -- I don't believe you get this from beginthreadex().

The only other way I can think of is to give each thread an event it can signal to let the main thread know when it's done, so rather than waiting on the handle you wait on the event. So, each thread has 2 events, one it monitors to see when it's being requested to terminate and another that it then sets as the last thing it does before it terminates to let the main thread know it's done. The main thread sets the event for the thread to tell it to stop and then waits on the other event to signal back when it's done. Its like me telling you to stop and you shouting back when you have.

Does that make sense?
0
How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

 
LVL 86

Assisted Solution

by:jkr
jkr earned 250 total points
Comment Utility
'_beginthreadex()' returns a thread handle, 'disguised' as a 'unsigned long' - just cast it (code taken from http://msdn.microsoft.com/en-us/library/ms235302(VS.80).aspx - "Multithreading and Locales", coincidentially that one applies exactly to your problem):
// multithread_locale_1.cpp

// compile with: /EHsc /MD

#include <clocale>

#include <cstdio>

#include <locale>

#include <process.h>

#include <windows.h>
 

#define NUM_THREADS 2

using namespace std;
 

unsigned __stdcall RunThreadA(void *params);

unsigned __stdcall RunThreadB(void *params);
 

BOOL localeSet = FALSE;

HANDLE printMutex = CreateMutex(NULL, FALSE, NULL);
 

int main()

{

    HANDLE threads[NUM_THREADS];
 

    unsigned aID;

    threads[0] = (HANDLE)_beginthreadex(

        NULL, 0, RunThreadA, NULL, 0, &aID);
 

    unsigned bID;

    threads[1] = (HANDLE)_beginthreadex(

        NULL, 0, RunThreadB, NULL, 0, &bID);
 

    WaitForMultipleObjects(2, threads, TRUE, INFINITE);
 

    printf_s("[Thread main] Per-thread locale is NOT enabled.\n");

    printf_s("[Thread main] CRT locale is set to \"%s\"\n",

        setlocale(LC_ALL, NULL));

    printf_s("[Thread main] locale::global is set to \"%s\"\n",

        locale().name().c_str());
 

    CloseHandle(threads[0]);

    CloseHandle(threads[1]);

    CloseHandle(printMutex);
 

    return 0;

}
 

unsigned __stdcall RunThreadA(void *params)

{

    _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);

    setlocale(LC_ALL, "french");

    localeSet = TRUE;
 

    WaitForSingleObject(printMutex, INFINITE);

    printf_s("[Thread A] Per-thread locale is enabled.\n");

    printf_s("[Thread A] CRT locale is set to \"%s\"\n",

        setlocale(LC_ALL, NULL));

    printf_s("[Thread A] locale::global is set to \"%s\"\n\n",

        locale().name().c_str());

    ReleaseMutex(printMutex);
 

    return 1;

}
 

unsigned __stdcall RunThreadB(void *params)

{

    while (!localeSet)

        Sleep(100);
 

    WaitForSingleObject(printMutex, INFINITE);

    printf_s("[Thread B] Per-thread locale is NOT enabled.\n");

    printf_s("[Thread B] CRT locale is set to \"%s\"\n",

        setlocale(LC_ALL, NULL));

    printf_s("[Thread B] locale::global is set to \"%s\"\n\n",

        locale().name().c_str());

    ReleaseMutex(printMutex);
 

    return 1;

}

Open in new window

0
 
LVL 40

Expert Comment

by:evilrix
Comment Utility
>> _beginthreadex()' returns a thread handle, 'disguised' as a 'unsigned long' - just cast it
Interesting info, thanks for that jkr.

>> coincidentially that one applies exactly to your problem
Almost, it doesn't signal the threads to terminate it just spins them up and waits for them to stop. As I understand it this isn't quite what's required; rather, the main thread needs to signal the others to terminate... is that right highqllc? If it is you could deploy the pattern I described above { http:#21759453 } but I should point out this wouldn't have been possible without the info jkr has posted above.
0
 
LVL 40

Expert Comment

by:evilrix
Comment Utility
Below is a brief example of the pattern I described in my first post.

This should be considered a conceptual example only.  I've made no real attempt at error handling, preferring to preserve brevity for the sake of clarity.

I hope this helps.

-Rx.
#include <iostream>

#include <vector>

#include <cstdlib>

#include <windows.h>

#include <process.h>
 

typedef std::vector<HANDLE> thread_handles_t;
 

HANDLE hEvent = 0;

HANDLE hMutex = 0;
 

void ThreadProc(void * pv)

{

	int nID = reinterpret_cast<int>(pv);
 

	WaitForSingleObject(hMutex, INFINITE);

	std::cerr << "Started thread " << nID << std::endl;

	ReleaseMutex(hMutex);
 

	while(WAIT_TIMEOUT == WaitForSingleObject(hEvent, 0)) { Sleep(0); }
 

	WaitForSingleObject(hMutex, INFINITE);

	std::cerr << "Stopped thread " << nID << std::endl;

	ReleaseMutex(hMutex);

}
 

int main()

{

	hEvent = CreateEvent(NULL, true, false, NULL);

	hMutex = CreateMutex(NULL, false, NULL);
 

	if(hEvent && hMutex)

	{

		thread_handles_t thread_handles;

		for(int nID = 0 ; nID < 20 ; ++nID)

		{

			uintptr_t iptr = _beginthread(ThreadProc, 0, reinterpret_cast<void *>(nID));
 

			if(-1 == iptr)

			{

				std::cerr << "Unable to create thread " << nID << std::endl;

			}
 

			thread_handles.push_back(reinterpret_cast<HANDLE>(iptr));

		}
 

		system("pause");

		SetEvent(hEvent);

		WaitForMultipleObjects(thread_handles.size(), &thread_handles[0], true, INFINITE);

		system("pause");

	}
 

	CloseHandle(hMutex);

	CloseHandle(hEvent);

}

Open in new window

0
 

Author Comment

by:highqllc
Comment Utility
hey guys,

I have discovered some new complexity in my project that is outside the scope of this question. It has to do more with the communication aspect of the object, and how to pass ack messages back in forth to indicate that shutting down is in progress.

However, the suggestions you have provided seem very appropriate for the simplified question that I asked and will serve as a nice reference.  So, I'm going to close this question and perhaps ask a new on on the problems I'm currently grappling with.

Thanks
0

Featured Post

How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

Join & Write a Comment

  Included as part of the C++ Standard Template Library (STL) is a collection of generic containers. Each of these containers serves a different purpose and has different pros and cons. It is often difficult to decide which container to use and …
Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall…
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now