[ Home | Syllabus | Course Notes | Assignments | Search]
Two kinds of threads in MFC (both built on win32 thread see saw last class)
Worker thread: In MFC, makes win 32 thread compatible
with MFC - performs sanity checks, keeps state information, ensures thread
safe access to C run-time library.
Communications is by setting global variables and using synchronization
primitives (typical OS stuff)
Create using AfxBeginThread which creates a CWinThread Object,
parameters are the thread function (must be static or global) and single
parameter.
UI thread: thread wrapped in MFC class derived from
CWinThread which has a message loop. preferred communications is by messages
instead of setting global variables and using synchronization primitives
Also created using AfxBeginThread but with runtime derived
class as first parameter.
Threads can be suspended, resumed or put to sleep.
Thread termination:
Worker thread execute "return" or calls AfxEndThread,
Another thread can call "::TerminateThread" but this may lead to
resource allocation leaks
Better to signal thread or set global variable that you want it to terminate
itself
UI thread: send it a WM_QUIT message
Issue with Autodeletion of CWinThread (p. 992). If you want to access this objects state (why - for return code for one) you must prevent autodeletion - there are two ways to do this. Create thread suspended so you guarantee the ability to set thread state correctly (pp 993-994) - in the off chance it might run and complete and autodelete
Also Create- suspended if you want to wait for thread to end - (like we did in the previous class) because we need handle to non-deleted object
Thread priority discussed pp 999-1000.
Contains common "C" function calls (and even if you don't call them MFC might).
There are 6 versions (mostly visual studio does this right - only issue is if you are integrating "C" code)
Libc.lib: single thread, static link, release build (/ML)
Libcd.lib: single thread, static link, debug build (/MLd)
Libcmt.lib: multi thread, static link, release build (/MT)
Libcmtd.lib: multi thread, static link, debug build (/MTd)
Msvcrt.lib: single/multi thread, dynamic link, release build (/MD)
Msvcrtd.lib: single/multi thread, dynamic link, debug build (/MDd)
Debug versions have extensive error checking using "Assert"
NOTE: it is troublesome to call MFC member functions across thread boundaries becuase sometimes some of the object state is in the thread that created the object. A HANDLE IS NOT A POINTER
Design Rule: do all UI manipulation in the main thread.
// SieveDlg.h
#define WM_USER_THREAD_FINISHED WM_USER+0x100 UINT ThreadFunc (LPVOID pParam); int Sieve (int nMax); typedef struct tagTHREADPARMS { int nMax; HWND hWnd; } THREADPARMS;
//SieveDlg.cpp
void CSieveDlg::OnStart() { int nMax = GetDlgItemInt (IDC_MAX); if (nMax < 10) { MessageBox (_T ("The number you enter must be 10 or higher")); GetDlgItem (IDC_MAX)->SetFocus (); return; } SetDlgItemText (IDC_RESULT, _T ("")); GetDlgItem (IDC_START)->EnableWindow (FALSE); THREADPARMS* ptp = new THREADPARMS; ptp->nMax = nMax; ptp->hWnd = m_hWnd; AfxBeginThread (ThreadFunc, ptp); } LONG CSieveDlg::OnThreadFinished (WPARAM wParam, LPARAM lParam) { SetDlgItemInt (IDC_RESULT, (int) wParam); GetDlgItem (IDC_START)->EnableWindow (TRUE); return 0; } ///////////////////////////////////////////////////////////////////////////// // Global functions UINT ThreadFunc (LPVOID pParam) { THREADPARMS* ptp = (THREADPARMS*) pParam; int nMax = ptp->nMax; HWND hWnd = ptp->hWnd; delete ptp; int nCount = Sieve (nMax); ::PostMessage (hWnd, WM_USER_THREAD_FINISHED, (WPARAM) nCount, 0); return 0; } int Sieve(int nMax) { PBYTE pBuffer = new BYTE[nMax + 1]; // could be single bit ::FillMemory (pBuffer, nMax + 1, 1); int nLimit = 2; while (nLimit * nLimit < nMax) // what does this loop do? nLimit++; for (int i=2; i<=nLimit; i++) { if (pBuffer[i]) { for (int k=i + i; k<=nMax; k+=i)// what does this loop do? pBuffer[k] = 0; } } int nCount = 0; for (i=2; i<=nMax; i++) if (pBuffer[i]) nCount++; delete[] pBuffer; return nCount; }
MFC Classes are thread safe at the class level NOT the object level
You can wrap a class functions using critical sections to make objects thread safe but there will be a performance penalty.
Remember that if another thread has a pointer to your object - then you may need synchornization.