第十一章 多任务与多线程

Click here to load reader

Upload: olin

Post on 24-Jan-2016

184 views

Category:

Documents


0 download

DESCRIPTION

第十一章 多任务与多线程. 所谓多任务就是指在一个计算机系统平台上一个以上的 任务 被 同时 执行。在 单 CPU 的单一计算机平台上,多任务是由单个 CPU 分时 完成的;在 多 CPU 的单一计算机上,多任务是由多个 CPU 协同并行 (包括部分分时)完成的;在 分布式系统 中, 多 任务 是分别由系统中的 多台计算机独立 或 协同并行 完成的。 多 任务 的表现形式可以分为两种: ⑴ 系统平台上的多任务 :同时运行的 多个进程 。这些进程之间 可以相关或互不相关。 ⑵ 进程中的多任务 :在同一进程中同时运行的 多个线程 ,一般 - PowerPoint PPT Presentation

TRANSCRIPT

  • CPU CPU CPU CPU

  • Win32 API Windows

  • 11.1 Windows 32 Windows 11.1.1 MFC MSG massage;while(::GetMessage(&message, NULL, 0, 0)){ ::TranslateMessage(&message); ::DispatchMessage(&message);}

  • API GetMessage Windows massage API TranslateMessage WM_KEYDOWN ASCII WM_CHAR API DispatchMessage MFC

  • MFC MFC DispatchMessage

  • 11.1.2 DispatchMessage Win32

  • MSG massage;while(::PeekMessage(&message, NULL, 0, 0, PM_REMOVE)){ ::TranslateMessage(&message); ::DispatchMessage(&message);}

  • PeekMessage GetMessage PeekMessage PeekMessage PeekMessage PeekMessage API DispatchMessage

  • 11.2 Windows 100 100 1 ClassWizard WM_TIMER 2 CWnd::SetTimer 3 CWnd::KillTimer

  • Windows 100 Windows WM_TIMER Windows

  • 11.3 Windows CWinApp::OnIdle CWinThread::OnIdle OnIdle OnIdle virtual BOOL OnIdle( LONG lCount );1 OnIdle

  • 2 OnIdle 3 OnIdle false OnIdle OnIdle OnIdle OnIdle true OnIdle 4 lCount OnIdle OnIdle lCount 1 lCount 0lCount OnIdle lCount

  • 5 OnIdle WM_ENTERIDLE OnEnterIdle

  • OnIdle 1 OnIdle OnIdle 2 OnIdle OnIdle CMyApp::OnIdle( LONG lCount ) {// return CWinApp::OnIdle( lCount ); }

  • 3 OnIdle CMyApp::OnIdle( LONG lCount ) {CWinApp::OnIdle( lCount );// return TRUE; }

  • 11.4 Windows

  • Windows CWinThread CWinThread CWinApp CWinThread

  • 11.4.1 UINT 32 LPVOID UINT ComputeThreadProc( LPVOID pParam ){ // Do thread processing return 0;} AfxBeginThread

  • CWinThread RUNTIME_CLASS CWinThread protected public Run Run

  • Sleep m_pMainwnd .cpp AfxBeginThread CreateThread MFC AfxBeginThread AfxBeginThread

  • CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc, LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );

    CWinThread* AfxBeginThread( CRuntimeClass* pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );

  • 1 pParam 2 nPriority

    0THREAD_PRIORITY_NORMALTHREAD_PRIORITY_ABOVE_NORMALTHREAD_PRIORITY_BELOW_NORMALTHREAD_PRIORITY_HIGHESTTHREAD_PRIORITY_LOWESTTHREAD_PRIORITY_IDLETHREAD_PRIORITY_TIME_CRITICAL

  • Windows nStackSize nStackSize = 0 dwCreateFlags

    lpSecurityAttrs lpSecurityAttrs = 0 Windows NT 0

    0CREATE_SUSPENDED ResumeThread

  • 11.4.2

  • 1232

  • MFC

  • 11.4.2.1 if ()else

  • MFC CCriticalSection

    CCriticalSection BOOL Lock(); BOOL Lock( DWORD dwTimeout ); 00 dwTimeoutLock CObject

  • Lock CMutex CCriticalSectionvirtual BOOL Unlock(); CCriticalSection 00 CCriticalSection CCriticalSection Unlock

  • CCriticalSection CSingleLock CCriticalSection m_criticalSection; CSingleLock m_singleLock( &m_criticalSection ); CCriticalSection CMultiLock CCriticalSection m_criticalSections[10]; CMultiLock m_multiLock( &m_criticalSections ); CSingleLock CMultiLock Lock Unlock

  • CSingleLock CMultiLock CSingleLock CSyncObject CSingleLock CMultiLock CSyncObject CMultiLock CMultiLock CEvent

  • 123423132

  • 1 Mthread

  • 1 Mthread SDI 2 IDD_COMPUTE CComputeDlg

    CProgressCtrlIDC_PROGRESS1 CButtonIDC_START CButtonIDCANCEL

  • 3 CComputeDlg

    void CComputeDlg::OnStart() { m_nTimer = SetTimer( 1, 100, NULL ); // 100 ms ASSERT( m_nTimer != 0 ); GetDlgItem( IDC_START )->EnableWindow( FALSE ); AfxBeginThread( ComputeThreadProc, GetSafeHwnd(), THREAD_PRIORITY_NORMAL ); // }

    OnStartOnCancelOnTimerOnThreadFinished

  • AfxBeginThread ComputeThreadProc void CComputeDlg::OnCancel() { if (g_nCount == 0) { // prior to Start button CDialog::OnCancel(); } else { // computation in progress g_nCount = nMaxCount; // Force thread to exit } }

  • g_nCount nMaxCount g_nCount = nMaxCount void CComputeDlg::OnTimer( UINT nIDEvent ) { CProgressCtrl* pBar = (CProgressCtrl*) GetDlgItem(IDC_PROGRESS1); pBar->SetPos( g_nCount * 100 / nMaxCount ); }

  • SetPos LRESULT CComputeDlg::OnThreadFinished( WPARAM wParam, LPARAM lParam ) { KillTimer(m_nTimer); CDialog::OnOK(); return 0; }

  • 4 UINT ComputeThreadProc( LPVOID pParam ) { volatile int nTemp; // volatile else compiler optimizes too much for (g_nCount = 0; g_nCount < CComputeDlg::nMaxCount; ::InterlockedIncrement( (long*) &g_nCount) ) {for (nTemp = 0; nTemp < 10000; nTemp++) { // uses up CPU cycles} } // WM_THREADFINISHED is user-defined message ::PostMessage((HWND) pParam, WM_THREADFINISHED, 0, 0); g_nCount = 0; return 0; // ends the thread }

  • volatile nTemp Win32 InterlockedIncrement g_nCount 1 #define WM_THREADFINISHEDWM_USER + 5

  • 5 CMThreadView CComputeDlg void CMThreadView::OnLButtonDown( UINT nFlags, CPoint point ) { CComputeDlg dlg; dlg.DoModal(); }

    6 Mthread

  • 2 Win32 InterlockedIncrement1 Mthread1 1 CHMS

  • class CHMS { private: int m_nHr, m_nMn, m_nSc; CCriticalSection m_cs; public: CHMS() : m_nHr( 0 ), m_nMn( 0 ), m_nSc( 0 ) { } ~CHMS() {} void SetTime( int nSecs ) { m_cs.Lock(); m_nSc = nSecs % 60;

  • m_nMn = (nSecs / 60) % 60; m_nHr = nSecs / 3600; m_cs.Unlock(); }int GetTotalSecs(){int nTotalSecs; m_cs.Lock(); nTotalSecs = m_nHr * 3600 + m_nMn * 60 + m_nSc; m_cs.Unlock(); return nTotalSecs;}

  • void IncrementSecs() {SetTime( GetTotalSecs() + 1 ); } CString GetHr_Mn_Sc() { CString str;m_cs.Lock(); str.Format( "%2d:%2d:%2d",m_nHr, m_nMn, m_nSc ); m_cs.Unlock(); return str; } };

  • CHMS CCriticalSection m_cs CHMS SetTimeGetTotalSecs IncrementSecs

  • 2 g_nCount CHMS CComputeDlg g_nCount void CComputeDlg::OnCancel() { if (g_nCount.GetTotalSecs() == 0) // prior to Start buttonCDialog::OnCancel(); else // Force thread to exitg_nCount.SetTime( nMaxCount ); } void CComputeDlg::OnTimer( UINT nIDEvent ) { CProgressCtrl* pBar =(CProgressCtrl*) GetDlgItem(IDC_PROGRESS1); pBar->SetPos( g_nCount.GetTotalSecs() * 100 / nMaxCount ); }

  • 3 UINT ComputeThreadProc( LPVOID pParam ) { volatile int nTemp; // volatile else compiler optimizes too much for (g_nCount.SetTime(0); g_nCount.GetTotalSecs() < CComputeDlg::nMaxCount; g_nCount.IncrementSecs()) {for (nTemp = 0; nTemp < 100000; nTemp++) { // uses up CPU cycles}SetDlgItemText((HWND) pParam, IDC_TIME, g_nCount.GetHr_Mn_Sc()); }

  • // WM_THREADFINISHED is user-defined message ::PostMessage((HWND) pParam, WM_THREADFINISHED, 0, 0); g_nCount.SetTime(0); return 0; // ends the thread }4 CHMS

    5 Mthread1

  • 11.4.2.2

  • CMutex CMutex

  • An object of class CMutex represents a mutex a synchronization object that allows one thread mutually exclusive access to a resource. Mutexes are useful when only one thread at a time can be allowed to modify data or some other controlled resource. For example, adding nodes to a linked list is a process that should only be allowed by one thread at a time. By using a CMutex object to control the linked list, only one thread at a time can gain access to the list.CObject

  • To use a CMutex object, construct the CMutex object when it is needed. Specify the name of the mutex you wish to wait on, and that your application should initially own it. You can then access the mutex when the constructor returns. Call CSyncObject::Unlock when you are done accessing the controlled resource.An alternative method for using CMutex objects is to add a variable of type CMutex as a data member to the class you wish to control. During construction of the controlled object, call the constructor of the CMutex data member specifying if the mutex is initially owned, the name of the mutex (if it will be used across process boundaries), and desired security attributes.

  • To access resources controlled by CMutex objects in this manner, first create a variable of either type CSingleLock or type CMultiLock in your resources access member function. Then call the lock objects Lock member function (for example, CSingleLock::Lock). At this point, your thread will either gain access to the resource, wait for the resource to be released and gain access, or wait for the resource to be released and time out, failing to gain access to the resource. In any case, your resource has been accessed in a thread-safe manner. To release the resource, use the lock objects Unlock member function (for example, CSingleLock::Unlock), or allow the lock object to fall out of scope.

  • CMutex( BOOL bInitiallyOwn = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );bInitiallyOwn CMutex TRUE lpszName lpszName lpszName = NULL

  • lpszName lpsaAttribute lpsaAttribute = NULL MSDN Win32 Programmers Referenc SECURITY_ATTRIBUTES CMutex CSingleLock CMutex m_mutex;CSingleLock m_singleLock( &m_mutex );

  • CMutex CMultiLock CMutex m_mutexs[10];CMultiLock m_multiLock( &m_mutexs ); CSingleLock CMultiLock Lock Unlock

  • 3 mutexes

  • 1 CWinThreadCExampleThreadCCounterThreadCDisplayThreadMFC

  • class CExampleThread : public CWinThread {DECLARE_DYNCREATE(CExampleThread) protected:CExampleThread(); public:CMutexesDlg* m_pOwner; // BOOL m_bDone;// // void SetOwner(CMutexesDlg* pOwner) { m_pOwner = pOwner; }; };

  • CExampleThread::CExampleThread() {m_bDone = FALSE;m_pOwner = NULL;m_bAutoDelete = FALSE;// }

  • class CCounterThread : public CExampleThread {DECLARE_DYNCREATE(CCounterThread) protected:CCounterThread(); // protected constructor used by dynamic creation public:virtual BOOL InitInstance();virtual int Run();virtual ~CCounterThread();DECLARE_MESSAGE_MAP() };

  • int CCounterThread::Run() {BOOL fSyncChecked;// unsigned int nNumber;// if (m_pOwner == NULL) return -1;CSingleLock sLock(&(m_pOwner->m_mutex));// while (!m_bDone)// { // fSyncChecked = m_pOwner->IsDlgButtonChecked(IDC_SYNCHRONIZE); if (fSyncChecked) sLock.Lock();//

  • // _stscanf((LPCTSTR) m_pOwner->m_strNumber, _T("%d"), &nNumber); nNumber++; m_pOwner->m_strNumber.Empty(); while (nNumber != 0) { m_pOwner->m_strNumber += (TCHAR) ('0' + nNumber%10); // Sleep() // CPU // Sleep(0); nNumber /= 10; }

  • m_pOwner->m_strNumber.MakeReverse();// if (fSyncChecked) sLock.Unlock();// // if (m_pOwner->IsDlgButtonChecked(IDC_SHOWCNTRTHRD))m_pOwner->AddToListBox(_T("Cntr: Increment"));}m_pOwner->PostMessage(WM_CLOSE, 0, 0L);// return 0; }

  • BOOL CCounterThread::InitInstance() { // TODO: perform and per-thread initialization herereturn TRUE; }

  • class CDisplayThread : public CExampleThread {DECLARE_DYNCREATE(CDisplayThread) protected:CDisplayThread(); // protected constructor used by dynamic creation public:virtual BOOL InitInstance();virtual int Run();virtual ~CDisplayThread();DECLARE_MESSAGE_MAP() };

  • int CDisplayThread::Run() {BOOL fSyncChecked;// CString strBuffer;// if (m_pOwner == NULL) return -1;CSingleLock sLock(&(m_pOwner->m_mutex));// while (!m_bDone)// { // fSyncChecked = m_pOwner->IsDlgButtonChecked(IDC_SYNCHRONIZE); if (fSyncChecked) sLock.Lock();//

  • // strBuffer = _T("Dspy: "); strBuffer += m_pOwner->m_strNumber; if (fSyncChecked) sLock.Unlock();// // m_pOwner->AddToListBox(strBuffer);}m_pOwner->PostMessage(WM_CLOSE, 0, 0L);// return 0; }

  • BOOL CDisplayThread::InitInstance() {// TODO: perform and per-thread initialization herereturn TRUE; }

  • 2 class CMutexesDlg : public CDialog{public:CString m_strNumber;// CMutex m_mutex;// CCounterThread* m_pCounterThread;// CDisplayThread* m_pDisplayThread;// };

  • m_mutex CMutexesDlgCMutexesDlg::CMutexesDlg( CWnd* pParent /*=NULL*/ ): CDialog( CMutexesDlg::IDD, pParent ), m_mutex( FALSE, NULL ), m_strNumber( _T("0") ){m_hIcon = AfxGetApp()->LoadIcon( IDR_MAINFRAME );m_pCounterThread = NULL;m_pDisplayThread = NULL;}

  • OnInitDialog BOOL CMutexesDlg::OnInitDialog() {m_pDisplayThread = (CDisplayThread*) AfxBeginThread(RUNTIME_CLASS(CDisplayThread), THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED); // m_pDisplayThread->SetOwner(this); // m_pDisplayThread->ResumeThread();//

  • m_pCounterThread = (CCounterThread*) AfxBeginThread(RUNTIME_CLASS(CCounterThread), THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);// m_pCounterThread->SetOwner(this);// m_pCounterThread->ResumeThread();// return TRUE; // return TRUE unless you set the focus to a control }

  • void CMutexesDlg::OnPriorityClassChange(){ DWORD dw; CComboBox* pBox = (CComboBox*) GetDlgItem(IDC_PRIORITYCLASS); int nCurSel = pBox->GetCurSel();

  • switch (nCurSel) { case 0:dw = IDLE_PRIORITY_CLASS; break; case 1: default: dw = NORMAL_PRIORITY_CLASS; break; case 2: dw = HIGH_PRIORITY_CLASS; break; } SetPriorityClass(GetCurrentProcess(), dw);}

  • void CMutexesDlg::OnPriorityChangeDisp(){ OnPriorityChange(IDC_DSPYTHRDPRIORITY);}void CMutexesDlg::OnPriorityChangeCntr(){ OnPriorityChange(IDC_CNTRTHRDPRIORITY);}void CMutexesDlg::OnPriorityChange(UINT nID){ ASSERT(nID == IDC_CNTRTHRDPRIORITY || nID == IDC_DSPYTHRDPRIORITY); DWORD dw;

  • CComboBox* pBox = (CComboBox*) GetDlgItem(nID); int nCurSel = pBox->GetCurSel(); switch (nCurSel) { case 0: dw = (DWORD) THREAD_PRIORITY_IDLE;break; case 1: dw = (DWORD) THREAD_PRIORITY_LOWEST;break; case 2: dw =(DWORD)THREAD_PRIORITY_BELOW_NORMAL;break;

  • case 3: default:dw = (DWORD) THREAD_PRIORITY_NORMAL;break; case 4:dw = (DWORD)THREAD_PRIORITY_ABOVE_NORMAL;break; }if (nID == IDC_CNTRTHRDPRIORITY)m_pCounterThread->SetThreadPriority(dw);elsem_pDisplayThread->SetThreadPriority(dw);}

  • Run fSyncChecked = m_pOwner->IsDlgButtonChecked(IDC_SYNCHRONIZE);if (fSyncChecked) sLock.Lock();// // if (fSyncChecked) sLock.Unlock();//

  • Run // if (m_pOwner->IsDlgButtonChecked(IDC_SHOWCNTRTHRD))m_pOwner->AddToListBox(_T("Cntr: Increment"));

  • SuspendThread OnPause void CMutexesDlg::OnPause() {CButton* pCheck = (CButton*) GetDlgItem(IDC_PAUSE);BOOL bPaused = ((pCheck->GetState() & 0x003) != 0);if (bPaused){ m_pCounterThread->SuspendThread(); m_pDisplayThread->SuspendThread();}

  • else{ m_pCounterThread->ResumeThread(); m_pDisplayThread->ResumeThread();} }

  • Run while m_bDone TRUE OnClose

  • void CMutexesDlg::OnClose() {int nCount = 0;// DWORD dwStatus;// // CButton* pCheck = (CButton*) GetDlgItem(IDC_PAUSE);BOOL bPaused = ((pCheck->GetState() & 0x003) != 0);if (bPaused == TRUE){ pCheck->SetCheck(0); m_pCounterThread->ResumeThread(); m_pDisplayThread->ResumeThread();}

  • // if (m_pCounterThread != NULL){ VERIFY(::GetExitCodeThread(m_pCounterThread->m_hThread, &dwStatus)); if (dwStatus == STILL_ACTIVE) { nCount++; m_pCounterThread->m_bDone = TRUE;// } else { delete m_pCounterThread;// m_pCounterThread = NULL; }}

  • // if (m_pDisplayThread != NULL){ VERIFY(::GetExitCodeThread(m_pDisplayThread->m_hThread, &dwStatus)); if (dwStatus == STILL_ACTIVE) { nCount++; m_pDisplayThread->m_bDone = TRUE; // } else { delete m_pDisplayThread;// m_pDisplayThread = NULL; }}

  • if (nCount == 0) CDialog::OnClose();else PostMessage( WM_CLOSE, 0, 0 ); }

    3 mutexes

  • 11.4.2.3 ; 1 1; 0 0 0

  • CSemaphore CSemaphore

    An object of class CSemaphore represents a semaphore a synchronization object that allows a limited number of threads in one or more processes to access a resource. A CSemaphore object maintains a count of the number of threads currently accessing a specified resource. CObject

  • Semaphores are useful in controlling access to a shared resource thatcan only support a limited number of users. The current count of the CSemaphore object is the number of additional users allowed.When the count reaches zero, all attempts to use the resource controlled by the CSemaphore object will be inserted into a system queue and wait until they either time out or the count rises above 0. The maximum number of users who can access the controlled resource at one time is specified during construction of the CSemaphore object.To use a CSemaphore object, construct the CSemaphore object when it is needed. Specify the name of the semaphore you wish to wait on, and that your application should initially own it. You can then access the semaphore when the constructor returns. Call CSyncObject::Unlock when you are done accessing the controlled resource.

  • An alternative method for using CSemaphore objects is to add a variable of type CSemaphore as a data member to the class you wish to control. During construction of the controlled object, call the constructor of the CSemaphore data member specifying the initial access count, maximum access count, name of the semaphore (if it will be used across process boundaries), and desired security attributes.To access resources contolled by CSemaphore objects in this manner, first create a variable of either type CSingleLock or type CMultiLock in your resources access member function. Then call the lock objects Lock member function (for example, CSingleLock::Lock). At this point, your thread will either gain access to the resource, wait for the resource to be released and gain access, or wait for the resource to be released and time out, failing to gain access to the resource. In any case, your resource has been accessed in a thread-safe manner.

  • To release the resource, use the lock objects Unlock member function (for example, CSingleLock::Unlock), or allow the lock object to fall out of scope.Alternatively, you can create a CSemaphore object stand-alone, and access it explicitly before attempting to access the controlled resource. This method, while clearer to someone reading your source code, is more prone to error.

  • CSemaphore( LONG lInitialCount = 1, LONG lMaxCount = 1, LPCTSTR lpstrName = NULL, LPSECURITY_ATTRIBUTES lpsaAttributes = NULL );lInitialCount 0 lMaxCountlMaxCount 0lpstrName lpstrName lpszName = NULL

  • lpstrName lpsaAttributes lpsaAttribute = NULL MSDN Win32 Programmers Referenc SECURITY_ATTRIBUTES CSingleLock CSemaphore m_semaphore;CSingleLock m_singleLock( &m_semaphore );

  • CSemaphore CMultiLock CSemaphore m_semaphores[10];CMultiLock m_multiLock( &m_semaphores ); CSingleLock CMultiLock Lock Unlock Lock 1 1 0 Lock 1Unlock1

  • n CSemaphore CSemaphore Lock CSingleLock Lock CSemaphore 0 CSemaphore 0 CSemaphore CSingleLock Unlock CSemaphore 1

  • 4 Semaphore 3 1 class CSemaphoreDlg : public CDialog {CSemaphore m_semaphore;// };

  • 2 CSemaphoreDlg::CSemaphoreDlg(CWnd* pParent /*=NULL*/): CDialog(CSemaphoreDlg::IDD, pParent), m_semaphore(1, 1), m_strNumber(_T("0")) { } m_semaphore

  • 3 Semaphore 4 CSemaphoreDlg::CSemaphoreDlg( CWnd* pParent /*=NULL*/ ): CDialog( CSemaphoreDlg::IDD, pParent ), m_semaphore(2, 2), m_strNumber(_T("0")) { }

  • 11.4.2.4 Windows

  • Windows Windows

  • MFC CEvent CEvent

    An object of class CEvent represents an event a synchronization object that allows one thread to notify another that an event has occurred. Events are useful when a thread needs to know when to perform its task. For example, a thread that copies data to a data archive would need to be notified when new data is available. By using a CEvent object to notify the copy thread when new data is available, the thread can perform its task as soon as possible.CObject

  • CEvent objects have two types: manual and automatic. A manual CEvent object stays in the state set by SetEvent or ResetEvent until the other function is called. An automatic CEvent object automatically returns to a nonsignaled (unavailable) state after at least one thread is released.To use a CEvent object, construct the CEvent object when it is needed. Specify the name of the event you wish to wait on, and that your application should initially own it. You can then access the event when the constructor returns. Call SetEvent to signal (make available) the event object and then call Unlock when you are done accessing the controlled resource.

  • An alternative method for using CEvent objects is to add a variable of type CEvent as a data member to the class you wish to control. During construction of the controlled object, call the constructor of the CEvent data member specifying if the event is initially signaled, the type of event object you want, the name of the event (if it will be used across process boundaries), and desired security attributes.To access a resource controlled by a CEvent object in this manner, first create a variable of either type CSingleLock or type CMultiLock in your resources access member function. Then call the lock objects Lock member function (for example, CMultiLock::Lock). At this point, your

  • thread will either gain access to the resource, wait for the resource to be released and gain access, or wait for the resource to be released and time out, failing to gain access to the resource. In any case, your resource has been accessed in a thread-safe manner. To release the resource, call SetEvent to signal the event object, and then use the lock objects Unlock member function (for example, CMultiLock::Unlock), or allow the lock object to fall out of scope.For more information on using CEvent objects, see the article Multithreading: How to Use the Synchronization Classes in Visual C++ Programmer's Guide.

  • CEvent( BOOL bInitiallyOwn = FALSE, BOOL bManualReset = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );bInitiallyOwn TRUE CMultiLock CSingleLock FALSE bManualReset TRUE FALSE

  • lpszName lpstrName NULL lpsaAttribute MSDN Win32 SDK Programmers Reference SECURITY_ATTRIBUTES

  • CEvent BOOL SetEvent(); 0 0 ResetEvent

  • BOOL PulseEvent(); 0 0 PulseEvent PulseEvent PulseEvent

  • BOOL ResetEvent(); 0 0 SetEvent virtual BOOL Unlock(); 0 0

  • 5 Event 3 1 class CEventsDlg : public CDialog { public:CString m_strNumber;// CEvent m_eventCntr;// CEvent m_eventDisp;// };

  • 2 CEventsDlg::CEventsDlg( CWnd* pParent /*=NULL*/ ): CDialog( CEventsDlg::IDD, pParent ), m_mutex( FALSE, NULL ), m_eventDisp( FALSE, TRUE ),// m_eventCntr( FALSE, TRUE ), // m_strNumber( _T("0") ){};

  • 3 IDC_EVENT_DISPTHREADIDC_EVENT_CNTRTHREAD

  • 4 void CEventsDlg::OnEventDisp() {CButton* pCheck = (CButton*) GetDlgItem(IDC_EVENT_DISPTHREAD);BOOL bEvent = ((pCheck->GetState() & 0x003) != 0);if(bEvent)m_eventDisp.SetEvent();elsem_eventDisp.ResetEvent();}

  • void CEventsDlg::OnEventCntr() {CButton* pCheck = (CButton*) GetDlgItem(IDC_EVENT_CNTRTHREAD);BOOL bEvent = ((pCheck->GetState() & 0x003) != 0);if(bEvent)m_eventCntr.SetEvent();elsem_eventCntr.ResetEvent();}

  • 5 OnClose// if (bPaused == TRUE){}

  • pCheck = (CButton*) GetDlgItem(IDC_EVENT_DISPTHREAD);BOOL bEvent = ((pCheck->GetState() & 0x003) != 0);if(bEvent == FALSE){pCheck->SetCheck(1);m_eventDisp.SetEvent();}pCheck = (CButton*) GetDlgItem(IDC_EVENT_CNTRTHREAD);bEvent = ((pCheck->GetState() & 0x003) != 0);if(bEvent == FALSE){pCheck->SetCheck(1);m_eventCntr.SetEvent();}

  • 6 Run int CDisplayThread::Run(){ // ::WaitForSingleObject(m_pOwner->m_eventDisp, INFINITE ); while (!m_bDone)// {// m_pOwner->AddToListBox(strBuffer);// ::WaitForSingleObject(m_pOwner->m_eventDisp, INFINITE ); } }

  • int CCounterThread::Run(){ // ::WaitForSingleObject(m_pOwner->m_eventCntr, INFINITE ); while (!m_bDone)// {// if (m_pOwner->IsDlgButtonChecked(IDC_SHOWCNTRTHRD))m_pOwner->AddToListBox(_T("Cntr: Increment"));// ::WaitForSingleObject(m_pOwner->m_eventCntr, INFINITE ); } }

  • 7 Event

  • 11.5 4

  • 1 AppWizard MultiTasks SDI CFormView ActiveX

  • 2 IDD_MULTITASKS_FORM

    Check Box IDC_CBONIDLE1 onIdle Thread1Check BoxIDC_CBONIDLE2onIdle Thread2Check BoxIDC_CBTHREAD1Thread1Check BoxIDC_CBTHREAD2Thread2

  • IDC_CBONIDLE1 m_bOnIdle1ValueBOOLIDC_CBONIDLE2m_bOnIdle2ValueBOOLIDC_CBTHREAD1m_bThread1ValueBOOLIDC_CBTHREAD2m_bThread2ValueBOOL

  • 3 CSpinner CObject

  • CSpinner class CSpinner : public CObject {public: CSpinner(); virtual ~CSpinner(); void Draw();// BOOL* GetContinue() { return m_bContinue; } // void SetContinue(BOOL* bContinue ) { m_bContinue = bContinue; } // CWnd* GetViewWnd() { return m_pViewWnd; } // void SetViewWnd( CWnd* pWnd ) { m_pViewWnd = pWnd; } // void SetLength( int iLength ) { m_iRadius = iLength; }// void SetPoint( CPoint pPoint ) { m_pCenter = pPoint; } //

  • private: int m_crColor;// int m_nMinute;// int m_iRadius;// CPoint m_pCenter;// static COLORREF m_crColors[8]; // CWnd* m_pViewWnd;// BOOL* m_bContinue;// };

  • m_crColors[8] CSpinner COLORREF CSpinner::m_crColors[8] = {RGB( 0, 0, 0 ),// BlackRGB( 0, 0,255 ),// BlueRGB( 0,255, 0 ),// GreenRGB( 0,255,255 ),// CyanRGB( 255, 0, 0 ),// RedRGB( 255, 0,255 ),// MagentaRGB( 255,255, 0 ),// YellowRGB( 255,255,255 )// White };

  • CSpinner::CSpinner(){ m_iRadius = 0; m_nMinute = 0; m_crColor = 0; // Nullfy the pointers m_pViewWnd = NULL; m_bContinue = NULL;}CSpinner::~CSpinner() { }

  • void CSpinner::Draw(){ CDC *pDC = m_pViewWnd->GetDC();// Get the pointer to the device context pDC->SetMapMode( MM_LOENGLISH ); // Set the mapping mode // Set the origination point CPoint org = m_pCenter; // Copy the spinner center pDC->SetViewportOrg( org.x, org.y ); // Set the viewport origination // Set the starting point CPoint pStartPoint; pStartPoint.x = m_iRadius / 2; pStartPoint.y = m_iRadius / 2;

  • // Calulate the angle of the next line double nRadians = (double)(m_nMinute * 6) * 0.017453292; // Set the end point of the line CPoint pEndPoint; pEndPoint.x = (int)(m_iRadius * sin(nRadians)); pEndPoint.y = (int)(m_iRadius * cos(nRadians)); CPen pen( PS_SOLID, 0, m_crColors[m_crColor] ); // Create the pen to use CPen* pOldPen = pDC->SelectObject( &pen); // Select the pen to use pDC->MoveTo( pEndPoint ); // Move to the starting point pDC->LineTo( pStartPoint ); // Draw the line to the end point pDC->SelectObject( pOldPen ); // Reselect the previous pen

  • // Increment the minute if(++m_nMinute == 60) { m_nMinute = 0; // If the minutes have gone full circle, reset to 0 // Increment the color if(++m_crColor == 8) m_crColor = 0; // If we' ve gone through all colors, start again } x = m_iRadius / 2 y = m_iRadius / 2}

  • x = m_iRadius / 2y = m_iRadius / 2x = m_pCenter.x y = m_pCenter.y

  • 4 CSpinner Draw CMultiTasksDoc CSpinner m_cSpin[4]private: CSpinner m_cSpin[4];// CalcPointInitSpinnersDoSpin OnNewDocument CalcPoint

  • void CMultiTasksDoc::CalcPoint( int nID, CSpinner *pSpin ) { RECT lWndRect; CPoint pPos; int iLength; CMultiTasksView *pWnd; pWnd = (CMultiTasksView*)pSpin->GetViewWnd();// Get a pointer to the view window pWnd->GetClientRect( &lWndRect ); // Get the display area rectangle iLength = lWndRect.right / 6; // Calculate the size of spinners

  • // Which spinner are we placing switch( nID ) { case 0:// Position the first spinnerpPos.x = ( lWndRect.right / 4 );pPos.y = ( lWndRect.bottom / 4 );break; case 1:// Position the second spinnerpPos.x = (( lWndRect.right / 4 ) * 3);pPos.y = ( lWndRect.bottom / 4 );break; case 2:// Position the third spinnerpPos.x = ( lWndRect.right / 4 );

  • pPos.y = (( lWndRect.bottom / 4 ) * 3);break; case 3:// Position the fourth spinnerpPos.x = (( lWndRect.right / 4 ) * 3);pPos.y = (( lWndRect.bottom / 4 ) * 3);break; } pSpin->SetLength( iLength ); // Set the size of the spinner pSpin->SetPoint( pPos ); // Set the location of the spinner } nID pSpin

  • CMultiTasksDoc InitSpinners void CMultiTasksDoc::InitSpinners() { int i; // Get the position of the view POSITION pos = GetFirstViewPosition(); if(pos != NULL) // Did we get a valid position

  • {CView* pView = GetNextView(pos); // Get a pointer to the view// Loop through the spinnersfor(i = 0; i < 4; i++){ m_cSpin[i].SetViewWnd(pView); // Set the pointer to the view // Initialize the continuation indicator m_cSpin[i].SetContinue(NULL); switch(i) { case 1: // Set the first thread continuation indicator m_cSpin[i].SetContinue(&((CMultiTasksView*)pView)->m_bThread1); break;

  • case 3: // Set the second thread continuation indicator m_cSpin[i].SetContinue(&((CMultiTasksView*)pView)->m_bThread2); break; } CalcPoint(i, &m_cSpin[i]); // Calculate the location of spinner} } }

  • OnNewDocument OnNewDocument CSpinner::InitSpinners BOOL CMultiTasksDoc::OnNewDocument() { if (!CDocument::OnNewDocument())return FALSE; // Initialize the spinners InitSpinners(); return TRUE; }

  • DoSpin CSpinner::Draw m_cSpin[4] void CMultiTasksDoc::DoSpin( int nIndex ) { // Spin the spinner m_cSpin[nIndex].Draw(); }

  • 5 OnIdle OnIdle ClassWizard CMultiTasksApp OnIdle BOOL CMultiTasksApp::OnIdle( LONG lCount ) { CWinApp::OnIdle(lCount); // Call the ancestor's idle processing POSITION pos = GetFirstDocTemplatePosition();// Get the position of the first document template if(pos) // Do we have a valid template position ? {

  • CDocTemplate* pDocTemp = GetNextDocTemplate(pos); // Get a pointer to the ducument templateif(pDocTemp) // Do we have a valid pointer ?{ // Get the position of the first document POSITION dPos = pDocTemp->GetFirstDocPosition(); if(dPos) // Do we have a valid document position { // Get the pointer to document CMultiTasksDoc* pDocWnd = (CMultiTasksDoc*)pDocTemp->GetNextDoc(dPos); if(pDocWnd) // Do we have a valid pointer ? { // Get the position of the view POSITION vPos = pDocWnd->GetFirstViewPosition();

  • if(vPos) // Do we have a valid view position { // Get the pointer to the view CMultiTasksView* pView = (CMultiTasksView*)pDocWnd->GetNextView(vPos); if(pView) // Do we have a valid pointer ? { if(pView->m_bOnIdle1) // Does the first idle?pDocWnd->DoSpin(0); // Yesif(pView->m_bOnIdle2) // Does the second idle?pDocWnd->DoSpin(2); // Yes }

  • } } }} } return TRUE; } CWinApp::OnIdle(lCount); return TRUE; pView->m_bOnIdle1 pView->m_bOnIdle2

  • void CMultiTasksView::OnCbonidle() { // Sync the variables with the dialog UpdateData(TRUE); } ClassWizard IDC_CBONIDLE1IDC_CBONIDLE2 ON_BN_CLICKED(IDC_CBONIDLE1, OnCbonidle) ON_BN_CLICKED(IDC_CBONIDLE2, OnCbonidle)

  • 6 UINT ThreadFunc(LPVOID pParam) { // Convert the argument to a pointer to the spinner for the thread CSpinner* lpSpin = (CSpinner*)pParam; // Get a pointer to the continuation flag BOOL* pbContinue = lpSpin->GetContinue(); // Loop while the continue flag is true while(*pbContinue) lpSpin->Draw();// Spin the spinner return 0; } MultiTasksDoc.cpp

  • AfxBeginThread CWinThread private: CWinThread* m_pSpinThread[2]; SuspendSpinner

  • void CMultiTasksDoc::SuspendSpinner( int nIndex, BOOL bSuspend ) { if(!bSuspend) // If suspending the thread {if(m_pSpinThread[nIndex]) // Is the pointer for the thread valid ?{ // get the handle for the thread HANDLE hThread = m_pSpinThread[nIndex]->m_hThread; // wait for the thread to die ::WaitForSingleObject(hThread, INFINITE);} } else// we are running the thread

  • {int iSpnr;switch(nIndex) // Which spinner to use{case 0: iSpnr = 1; break;case 1: iSpnr = 3; break;}// Start the thread, passing a pointer to spinnerm_pSpinThread [nIndex] = AfxBeginThread(ThreadFunc, (LPVOID)&m_cSpin[iSpnr]); } } nIndex bSuspend

  • IDC_CBTHREAD1 IDC_CBTHREAD2 OnCbthread1 OnCbthread2 void CMultiTasksView::OnCbthread1() { UpdateData( TRUE ); // Sync the variables with the dialog CMultiTasksDoc* pDocWnd = (CMultiTasksDoc*)GetDocument();// Get a pointer to the document ASSERT_VALID(pDocWnd); // Did we have a void pointer ? pDocWnd->SuspendSpinner(0, m_bThread1); // Suspend or start the spinner thread }

  • void CMultiTasksView::OnCbthread2() { UpdateData(TRUE); // Sync the variables with the dialog CMultiTasksDoc* pDocWnd = (CMultiTasksDoc*)GetDocument(); // Get a pointer to the document ASSERT_VALID(pDocWnd); // Did we have a void pointer ? pDocWnd->SuspendSpinner(1, m_bThread2); // Suspend or start the spinner thread }

  • 7

  • ClassWizard CMultiTasksView OnDestoy ON_WM_DESTROY()void CMultiTasksView::OnDestroy() { CFormView::OnDestroy(); if( m_bThread1 ) // Is the first thread running ? { m_bThread1 = FALSE; // Specify to stop the first thread // Get a pointer to the document CMultiTasksDoc* pDocWnd = (CMultiTasksDoc*)GetDocument(); ASSERT_VALID(pDocWnd); // Did we get a valid pointer pDocWnd->SuspendSpinner(0, m_bThread1); // Suspend the spinner }

  • if(m_bThread2) // Is the second thread running ? { m_bThread2 = FALSE; // Specify to stop the second thread // Get a pointer to the document CMultiTasksDoc* pDocWnd = (CMultiTasksDoc*)GetDocument(); ASSERT_VALID(pDocWnd); // Did we get a valid pointer pDocWnd->SuspendSpinner(1, m_bThread2); // Suspend the spinner thread }}

  • 8 UpdateCtrlPosition void CMultiTasksView::UpdateCtrlPosition() { CRect clientRect, ctrlRect, updateRect; // Get the rectangle of client area if(this == NULL)return; GetClientRect(&clientRect); CEdit* pOnidle1 = (CEdit*)GetDlgItem(IDC_CBONIDLE1); if(!pOnidle1) return;

  • CEdit* pOnidle2 = (CEdit*)GetDlgItem(IDC_CBONIDLE2); if(!pOnidle2)return; CEdit* pThread1 = (CEdit*)GetDlgItem(IDC_CBTHREAD1); if(!pThread1)return; CEdit* pThread2 = (CEdit*)GetDlgItem(IDC_CBTHREAD2); if(!pThread2)return; // Get the rectangle of the control pOnidle1->GetWindowRect(&ctrlRect); // Create the rectangle of the moved control updateRect = CRect(clientRect.TopLeft(), CSize(ctrlRect.Width(), ctrlRect.Height())); // Move the control to new position pOnidle1->MoveWindow(&updateRect);

  • // Get the rectangle of the control pOnidle2->GetWindowRect(&ctrlRect); // Create the rectangle of the moved control updateRect = CRect(CPoint(clientRect.left, clientRect.bottom / 2), CSize(ctrlRect.Width(), ctrlRect.Height())); // Move the control to new position pOnidle2->MoveWindow(&updateRect); // Get the rectangle of the control pThread1->GetWindowRect(&ctrlRect); // Create the rectangle of the moved control updateRect = CRect(CPoint(clientRect.right / 2, clientRect.top), CSize(ctrlRect.Width(), ctrlRect.Height()));

  • // Move the control to new position pThread1->MoveWindow(&updateRect); // Get the rectangle of the control pThread2->GetWindowRect(&ctrlRect); // Create the rectangle of the moved control updateRect = CRect(CPoint(clientRect.right/2, clientRect.bottom/2), CSize(ctrlRect.Width(), ctrlRect.Height())); // Move the control to new position pThread2->MoveWindow(&updateRect); }

  • OnSize WM_SIZE OnSize ON_WM_SIZE() void CMultiTasksView::OnSize(UINT nType, int cx, int cy) { CFormView::OnSize(nType, cx, cy); // Update the positions of check box for changed client araea UpdateCtrlPosition(); // Get a pointer to the document CMultiTasksDoc* pDocWnd=(CMultiTasksDoc*)GetDocument(); ASSERT_VALID(pDocWnd); // Did we get a valid pointer pDocWnd->OnNewDocument();// Create a new document }

  • 9 MultiTasks