執行緒通訊初探 (轉)

worldblog發表於2007-12-03
執行緒通訊初探 (轉)[@more@]  程式是執行中的,有獨立的,控制程式碼和其它的資源,一個獨立的程式可以包含多條路徑,即執行緒。一個可以被多個執行緒訪問,多個執行緒可以訪問同一個全域性變數。
  提供兩種執行緒,介面執行緒和輔助執行緒。使用者介面執行緒有視窗,因此有自己的訊息迴圈,輔助執行緒沒有視窗,不需要處理訊息。但是輔助執行緒非常有用而且很容易,比如程式在某個執行時間要完成多個(很笨重的)任務時,顯然,輔助執行緒的使用會使程式的執行大大的提高。但是,執行緒間的通訊是一個必須解決的問題。
  下面我們就來討論一下執行緒間的通訊的問題:
  一.執行緒的管理
  1.執行緒的啟動:
  在使用輔助執行緒時,我們必須為執行緒寫一個全域性函式,它的返回值必須為  UINT型別,而且必須有LPVOID型別的引數,啟動執行緒下面的函式:
  CWinThread* pThread=AfxBeginThread(
  AFX_THREADPPOC ThreadProc,
  LPVOID pParam,
  int nPriority,
  UINT nStackSize,
  D dwCreateFlags,
  LPSECURITY_ATTRIBUTES lpSecurityAttrs);
  全域性函式必須定義為 UINT ThreadProc(LPVOID pParam);
  AfxBeginThread會立即返回一個指向新建立的執行緒的指標,用來管理線  程,包括掛起和恢復執行緒的執行,但是執行緒物件沒有成員函式來中止執行緒的運  行。AfxBeginThread的第二個引數是一個32位的值,用來傳給全域性函式;第三  個引數用來設定執行緒的優先順序;而第四和第六個引數用來指定執行緒堆疊大小和  性,一般採用預設值0;第五個引數用來設定建立執行緒物件的方式,0為立  即執行,CREATE_SUSPEND為執行緒透過ResumeThread後才執行。
  而執行緒優先順序的設定和獲得可以透過下面的兩個函式來實現:
  pThread->SetThreadPriority(THREAD_PRIORITY_ABOVE_NOMAL);和
  int nPriority=pThread->GetThreadPriority();
  2.執行緒的中止:
  可以呼叫MFC的AfxEndThread函式;
  3.檢查執行緒是否結束:
  呼叫函式GetExitCodeThread,
  DWORD ExitCode ;
  ::GetExitCodeThread(pThread->m_hThread,&ExitCode );
  if(ExitCode==STILL_ACTIVE)
  //執行中
  else  //執行緒已經中止
  二.主執行緒和輔助執行緒的通訊
  主執行緒和輔助執行緒間的通訊方式有很多種,最簡單的就是利用全域性變數。  這裡利用訊息通訊是行不通的,因為輔助執行緒沒有訊息迴圈,不能夠利用  Windows訊息。
  下面我們用一個例子來說明。在例子中,我們寫一個非常笨的函式,如實  現500*3000*3000的加法資料處理函式Add(int nCount);
  我們在對話方塊上放置Start、Cancel按鈕和一個用來表示資料處理進度的進度條  。
  1.利用全域性變數來實現主執行緒和輔助執行緒的通訊:
  我們編寫全域性函式如下:
  UINT ThreadProc(LPVOID pParam)
  {
  nCount=0;//全域性變數
  while(nCount<500)
  {
  Add(nCount);
  ::InterlockedIncrement((long*)&ncount);
  }
  return 0;
  }
  函式InterlockIncrement阻塞其它的執行緒,當計數器遞增時防止其它的執行緒訪問nCount。
  2.利用訊息實現輔助執行緒和主執行緒的通訊:
  主執行緒有一個視窗,有訊息迴圈,我們可以在呼叫AfxBeginThread時把視窗控制程式碼傳遞給輔助執行緒,我們透過post方式傳遞訊息,在函式退出時,給視窗傳送一個訊息。
  重新編寫執行緒函式如下:
  int nCount=0;
  UINT ThreadProc(LPVOID pParam)
  {
   while(nCount<500)
   {
   ::InterlockedIncrement((long*)&ncount);
   Add(nCount);
   }
   ::PostMessage(
    (HWND)pParam,
    WM_THREADFINISHED,//使用者自定義訊息
    0,0);
    return 0;
  }
  編寫OnStart函式:
  void CThreadDlg::OnStart()
  {
   m_nTimer=SetTimer(1,100,NULL);//0.1秒
   ASSERT(m_nTimer!=0);
   GetDlgItem(IDC_START)->EnableWindow(FALSE);
   AfxBeginThread(ThreadProc,GetSafeHwnd(),THREAD_PROIRITY_NOMAL);
  }
  編輯OnCancel函式如下:
  void CThreadDlg::OnCancel()
  {
   if(nCount==0)
    CDialog::OnCancel();
   else nCount=500;
  }
  處理OnThreadFinished函式
  HRESULT CThreadDlg::OnThreadFinished(WPARAM wParam,LPARAM lParam)
  {
   CDialog::OnOk();
   return 0;
  }
  3.用事件使執行緒同步:
  利用WaitForSingle函式
  在stdafx.h中寫入下面一行
  #include //由於使用了事件
  宣告兩個全域性變數
  CEvent m_start,m_kill;
  在初始化函式中啟動執行緒;
  重新編寫OnStart函式:
  void CThreadDlg::OnStart()
  {
   m_nTimer=SetTimer(1,100,NULL);//0.1秒
   ASSERT(m_nTimer!=0);
   GetDlgItem(IDC_START)->EnableWindow(FALSE);
   m_start.SetEvent();
  } 
  重新編輯OnCancel函式如下:
  void CThreadDlg::OnCancel()
  {
   if(nCount==0)
    m_start.SetEvent();
   else m_kill.SetEvent();
  }
  編寫全域性函式如下:
  UINT ThreadProc(LPVOID pParam)
  {
   ::WaitForSingleObject(m_start,INFINITE);
   while(nCount<500)
   {
   Add(nCount);
   if(::WaitForSingleObject(m_start,0)=WAIT_OBJECT_0)
    break;
   }
   ::PostMessage(
    (HWND)pParam,
    WM_THREADFINISHED,//使用者自定義訊息
    0,0);
    return 0;
  } 
  其中第一個WaitForSingleObject的呼叫等待啟動事件,INFINITE使其等待直到啟動事件有訊號。第二個呼叫若有訊號,立即返回,中止執行緒。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-987652/,如需轉載,請註明出處,否則將追究法律責任。

相關文章