Check out these fragments of code, which are from a multithreaded application I wrote recently. The app creates a closure event thus : m_hCloseEvent = CreateEvent (NULL, TRUE, FALSE, "AG61_BN9_RMM_CLOSE"); and this event is used when closing down the threads, as we'll see below. This is the part that starts the threads : CWinThread * pNewThread; ...This code then appears inside a loop which sets up the threads... m_ateThreadTable [m_iThreadCount].iPortNum = iComNum; m_ateThreadTable [m_iThreadCount].iThreadState = 0; m_ateThreadTable [m_iThreadCount].hStopEvent = m_hCloseEvent; m_ateThreadTable [m_iThreadCount].hCreatorWindow = GetSafeHwnd(); m_ateThreadTable [m_iThreadCount].uFlagBits = 0; pNewThread = AfxBeginThread (WorkerRunFunction, (LPVOID)&(m_ateThreadTable [m_iThreadCount])); // If the new thread started ok, save it's pointer in our // table, and clear its auto-delete flag so we can shut it // down manually. MFC defaults to auto-deleting the thread // object as soon as its run function exits, which can be // problematic for the kind of controlled shutdown I want. if (pNewThread) { m_ateThreadTable [m_iThreadCount].pThread = pNewThread; pNewThread->m_bAutoDelete = FALSE; } So you can see that the code is setting up a table of structures with data about each thread, then calling AfxBeginThread to create the thread, passing it the address of the structure which contains information it will need internally, such as the handle of the application closure event. The thread has no problem accessing this structure, because all threads share the same address space. The applications main thread then saves a pointer to the worker thread, which it can use when shutting down to ensure that all threads close when instructed using the closure event : if any threads fails to shutdown before some timeout, the app can call TerminateThread to forcibly kill it. The worker thread run function looks like this (note the thread-local variables, which contain data specific to this thread): static UINT __declspec( thread ) mg_uState ; static int __declspec( thread ) mg_uFlags; // ------------------------------------------------------------------------ UINT WorkerRunFunction (LPVOID pParam) { HANDLE hComPort = 0; HANDLE aWaitHandles [2]; OVERLAPPED ovlRead = {0}; // Convert the generic void parameter pointer into a pointer // to the actual data type passed.   TThreadEntry * pData = (TThreadEntry*) pParam; ... Parameter validation code omitted... // Create the event used for overlapped i/o. This is a comms thread. ovlRead.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); // Our thread consists of a loop, waiting for either a character // on the serial port or closure of the app. Note that the closure // event is first, since WaitForMultipleObjects gives "precedence" // starting from the zeroth end of its handle array. aWaitHandles [0] = pData->hStopEvent; aWaitHandles [1] = ovlRead.hEvent; ... Comms code (port setup) omitted... while (!bStopped) { ....< could additionally check the closure event here >... if (ReadFile (...) { // received data, store it and look at the buffer. } else { dwErr = GetLastError(); switch (dwErr) { case ERROR_IO_PENDING : // Wait for either data to arrive, or this // thread to be terminated. dwErr = WaitForMultipleObjects (2, aWaitHandles, FALSE, INFINITE); switch (dwErr) { case WAIT_OBJECT_0: // CLOSE event bStopped = TRUE; break; case WAIT_OBJECT_0+1: // READ event GetOverlappedResult (hComPort, &ovlRead, &dwBytesRead, FALSE); ResetEvent (ovlRead.hEvent); ... < do some processing here >... Get the picture ? Our thread basically consists of a loop, which sleeps until either a character is received or the app closure event gets set by the main thread. So how does the main thread organise closure of its kiddies ? Like this : void CMyMainThreadDlg::DestroyThreads () { HANDLE ahTempHandles [MAX_PORTS]; int iHandles; // Now we have to wait _synchronously_ for all the threads to // run to completion. Make up an array of thread handles for // WaitForMultipleObjects to wait on. iHandles = 0; memset (ahTempHandles, 0, sizeof(ahTempHandles)); for (int i=0; i < MAX_PORTS; i++) { if (m_ateThreadTable [i].pThread) { ahTempHandles [iHandles]= (m_ateThreadTable [i].pThread)-> m_hThread; iHandles++; } } if (iHandles > 0) { // There are threads to shut down - set the closure event. SetEvent (m_hCloseEvent); switch (WaitForMultipleObjects (iHandles, ahTempHandles, TRUE, 5000)) { case WAIT_FAILED: wsprintf (m_szDebug, "WaitForMultipleObjects failed, GLE=%u", GetLastError()) ; DebugMessage (m_szDebug); break; case WAIT_OBJECT_0: DebugMessage ("All COM threads closed OK"); break; case WAIT_ABANDONED: DebugMessage ("Wait abandoned"); break; case WAIT_TIMEOUT: DebugMessage ("Timed out waiting for COM threads"); HandleLaggards (); break; default: break; } if (ResetEvent (m_hCloseEvent)) DebugMessage ("Reset close event for next use"); else { wsprintf (m_szDebug, "attempt to reset close event failed, GLE=%u", GetLastError()) ; DebugMessage (m_szDebug); } } else { DebugMessage ("No active threads, exiting"); } }