• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            yehao's Blog

            多線程編程之四——線程的同步

            Part of the red marker to add by YeHao,Order oneself to better understand.

            八、線程的同步

              雖然多線程能給我們帶來好處,但是也有不少問題需要解決。例如,對于像磁盤驅(qū)動器這樣獨占性系統(tǒng)資源,由于線程可以執(zhí)行進(jìn)程的任何代碼段,且線程的運行是由系統(tǒng)調(diào)度自動完成的,具有一定的不確定性,因此就有可能出現(xiàn)兩個線程同時對磁盤驅(qū)動器進(jìn)行操作,從而出現(xiàn)操作錯誤;又例如,對于銀行系統(tǒng)的計算機(jī)來說,可能使用一個線程來更新其用戶數(shù)據(jù)庫,而用另外一個線程來讀取數(shù)據(jù)庫以響應(yīng)儲戶的需要,極有可能讀數(shù)據(jù)庫的線程讀取的是未完全更新的數(shù)據(jù)庫,因為可能在讀的時候只有一部分?jǐn)?shù)據(jù)被更新過。

              使隸屬于同一進(jìn)程的各線程協(xié)調(diào)一致地工作稱為線程的同步。MFC提供了多種同步對象,下面我們只介紹最常用的四種:

            • 臨界區(qū)(CCriticalSection)
            • 事件(CEvent)
            • 互斥量(CMutex)
            • 信號量(CSemaphore)
               

            通過這些類,我們可以比較容易地做到線程同步。

            A、使用 CCriticalSection 類

              當(dāng)多個線程訪問一個獨占性共享資源時,可以使用“臨界區(qū)”對象。任一時刻只有一個線程可以擁有臨界區(qū)對象,擁有臨界區(qū)的線程可以訪問被保護(hù)起來的資源或代碼段,其他希望進(jìn)入臨界區(qū)的線程將被掛起等待,直到擁有臨界區(qū)的線程放棄臨界區(qū)時為止,這樣就保證了不會在同一時刻出現(xiàn)多個線程訪問共享資源。

            CCriticalSection類的用法非常簡單,步驟如下:
             

            1. 定義CCriticalSection類的一個全局對象(以使各個線程均能訪問),如CCriticalSection critical_section;
            2. 在訪問需要保護(hù)的資源或代碼之前,調(diào)用CCriticalSection類的成員Lock()獲得臨界區(qū)對象:
              critical_section.Lock();
                  
              在線程中調(diào)用該函數(shù)來使線程獲得它所請求的臨界區(qū)。如果此時沒有其它線程占有臨界區(qū)對象,則調(diào)用Lock()的線程獲得臨界區(qū);否則,線程將被掛起,并放入到一個系統(tǒng)隊列中等待,直到當(dāng)前擁有臨界區(qū)的線程釋放了臨界區(qū)時為止。
            3. 訪問臨界區(qū)完畢后,使用CCriticalSection的成員函數(shù)Unlock()來釋放臨界區(qū):
              critical_section.Unlock();
                  
              再通俗一點講,就是線程A執(zhí)行到critical_section.Lock();語句時,如果其它線程(B)正在執(zhí)行critical_section.Lock();語句后且critical_section. Unlock();語句前的語句時,線程A就會等待,直到線程B執(zhí)行完critical_section. Unlock();語句,線程A才會繼續(xù)執(zhí)行。

            下面再通過一個實例進(jìn)行演示說明。


            例程8 MultiThread8

            1. 建立一個基于對話框的工程MultiThread8,在對話框IDD_MULTITHREAD8_DIALOG中加入兩個按鈕和兩個編輯框控件,兩個按鈕的ID分別為IDC_WRITEW和IDC_WRITED,標(biāo)題分別為“寫‘W’”和“寫‘D’”;兩個編輯框的ID分別為IDC_W和IDC_D,屬性都選中Read-only;
            2. 在MultiThread8Dlg.h文件中聲明兩個線程函數(shù):
              UINT WriteW(LPVOID pParam);
                  UINT WriteD(LPVOID pParam);
                  
            3. 使用ClassWizard分別給IDC_W和IDC_D添加CEdit類變量m_ctrlW和m_ctrlD;
            4. 在MultiThread8Dlg.cpp文件中添加如下內(nèi)容:

              為了文件中能夠正確使用同步類,在文件開頭添加:
              #include "afxmt.h"
                  
              定義臨界區(qū)和一個字符數(shù)組,為了能夠在不同線程間使用,定義為全局變量:
              CCriticalSection critical_section;
                  char g_Array[10];
                  
              添加線程函數(shù):
              UINT WriteW(LPVOID pParam)
                  {
                  CEdit *pEdit=(CEdit*)pParam;
                  pEdit->SetWindowText("");
                  critical_section.Lock();
                  //鎖定臨界區(qū),其它線程遇到critical_section.Lock();語句時要等待
                  //直至執(zhí)行critical_section.Unlock();語句
                  for(int i=0;i<10;i++)
                  {
                  g_Array[i]=''W'';
                  pEdit->SetWindowText(g_Array);
                  Sleep(1000);
                  }
                  critical_section.Unlock();
                  return 0;
                  }
                  UINT WriteD(LPVOID pParam)
                  {
                  CEdit *pEdit=(CEdit*)pParam;
                  pEdit->SetWindowText("");
                  critical_section.Lock();
                  //鎖定臨界區(qū),其它線程遇到critical_section.Lock();語句時要等待
                  //直至執(zhí)行critical_section.Unlock();語句
                  for(int i=0;i<10;i++)
                  {
                  g_Array[i]=''D'';
                  pEdit->SetWindowText(g_Array);
                  Sleep(1000);
                  }
                  critical_section.Unlock();
                  return 0;
                  }
            5. 分別雙擊按鈕IDC_WRITEW和IDC_WRITED,添加其響應(yīng)函數(shù):
              void CMultiThread8Dlg::OnWritew()
                  {
                  CWinThread *pWriteW=AfxBeginThread(WriteW,
                  &m_ctrlW,
                  THREAD_PRIORITY_NORMAL,
                  0,
                  CREATE_SUSPENDED);
                  pWriteW->ResumeThread();
                  }
                  void CMultiThread8Dlg::OnWrited()
                  {
                  CWinThread *pWriteD=AfxBeginThread(WriteD,
                  &m_ctrlD,
                  THREAD_PRIORITY_NORMAL,
                  0,
                  CREATE_SUSPENDED);
                  pWriteD->ResumeThread();
                  }
              由于代碼較簡單,不再詳述。編譯、運行該例程,您可以連續(xù)點擊兩個按鈕,觀察體會臨界類的作用。

            B、使用 CEvent 類

              CEvent 類提供了對事件的支持。事件是一個允許一個線程在某種情況發(fā)生時,喚醒另外一個線程的同步對象。例如在某些網(wǎng)絡(luò)應(yīng)用程序中,一個線程(記為A)負(fù)責(zé)監(jiān)聽通訊端口,另外一個線程(記為B)負(fù)責(zé)更新用戶數(shù)據(jù)。通過使用CEvent 類,線程A可以通知線程B何時更新用戶數(shù)據(jù)。每一個CEvent 對象可以有兩種狀態(tài):有信號狀態(tài)和無信號狀態(tài)。線程監(jiān)視位于其中的CEvent 類對象的狀態(tài),并在相應(yīng)的時候采取相應(yīng)的操作。
              在MFC中,CEvent 類對象有兩種類型:人工事件和自動事件。一個自動CEvent 對象在被至少一個線程釋放后會自動返回到無信號狀態(tài);而人工事件對象獲得信號后,釋放可利用線程,但直到調(diào)用成員函數(shù)ReSetEvent()才將其設(shè)置為無信號狀態(tài)。在創(chuàng)建CEvent 類的對象時,默認(rèn)創(chuàng)建的是自動事件。 CEvent 類的各成員函數(shù)的原型和參數(shù)說明如下:

            1、CEvent(BOOL bInitiallyOwn=FALSE,
            BOOL bManualReset=FALSE,
            LPCTSTR lpszName=NULL,
            LPSECURITY_ATTRIBUTES lpsaAttribute=NULL);
            

             

            • bInitiallyOwn:指定事件對象初始化狀態(tài),TRUE為有信號,F(xiàn)ALSE為無信號;
            • bManualReset:指定要創(chuàng)建的事件是屬于人工事件還是自動事件。TRUE為人工事件,F(xiàn)ALSE為自動事件;
            • 后兩個參數(shù)一般設(shè)為NULL,在此不作過多說明。
            2、BOOL CEvent::SetEvent();
            
              將 CEvent 類對象的狀態(tài)設(shè)置為有信號狀態(tài)。如果事件是人工事件,則 CEvent 類對象保持為有信號狀態(tài),直到調(diào)用成員函數(shù)ResetEvent()將 其重新設(shè)為無信號狀態(tài)時為止。如果CEvent 類對象為自動事件,則在SetEvent()將事件設(shè)置為有信號狀態(tài)后,CEvent 類對象由系統(tǒng)自動重置為無信號狀態(tài)。

            如果該函數(shù)執(zhí)行成功,則返回非零值,否則返回零。
            3、BOOL CEvent::ResetEvent();
              該函數(shù)將事件的狀態(tài)設(shè)置為無信號狀態(tài),并保持該狀態(tài)直至SetEvent()被調(diào)用時為止。由于自動事件是由系統(tǒng)自動重置,故自動事件不需要調(diào)用該函數(shù)。如果該函數(shù)執(zhí)行成功,返回非零值,否則返回零。我們一般通過調(diào)用WaitForSingleObject函數(shù)來監(jiān)視事件狀態(tài)。前面我們已經(jīng)介紹了該函數(shù)。由于語言描述的原因,CEvent 類的理解確實有些難度,但您只要通過仔細(xì)玩味下面例程,多看幾遍就可理解。

            例程9 MultiThread9

            1. 建立一個基于對話框的工程MultiThread9,在對話框IDD_MULTITHREAD9_DIALOG中加入一個按鈕和兩個編輯框控件,按鈕的ID為IDC_WRITEW,標(biāo)題為“寫‘W’”;兩個編輯框的ID分別為IDC_W和IDC_D,屬性都選中Read-only;
            2. 在MultiThread9Dlg.h文件中聲明兩個線程函數(shù):
              UINT WriteW(LPVOID pParam);
                  UINT WriteD(LPVOID pParam);
                  
            3. 使用ClassWizard分別給IDC_W和IDC_D添加CEdit類變量m_ctrlW和m_ctrlD;
            4. 在MultiThread9Dlg.cpp文件中添加如下內(nèi)容:

              為了文件中能夠正確使用同步類,在文件開頭添加

              #include "afxmt.h"
                  
              定義事件對象和一個字符數(shù)組,為了能夠在不同線程間使用,定義為全局變量。
              //CEvent eventWriteD;//默認(rèn)為無信號狀態(tài)
              CEvent eventWriteD(TRUE);
              char g_Array[10];
              添加線程函數(shù):
              UINT WriteW(LPVOID pParam)
                  {
                  CEdit *pEdit=(CEdit*)pParam;
                  pEdit->SetWindowText("");
                  WaitForSingleObject(eventWriteD.m_hObject,INFINITE);//函數(shù)返回時事件自動重置為無信號狀態(tài)
              for(int i=0;i<10;i++) { g_Array[i]=''W''; pEdit->SetWindowText(g_Array); Sleep(1000); } eventWriteD.SetEvent();//將事件置為有信號狀態(tài),使其他線程可以獲得有信號狀態(tài) return 0; } UINT WriteD(LPVOID pParam) { CEdit *pEdit=(CEdit*)pParam; pEdit->SetWindowText(""); WaitForSingleObject(eventWriteD.m_hObject,INFINITE);//當(dāng)事件為無信號狀態(tài)時WaitForSingleObject會阻塞線程,走到事件有信號 for(int i=0;i<10;i++) { g_Array[i]=''D''; pEdit->SetWindowText(g_Array); Sleep(1000); }
              eventWriteD.SetEvent(); return 0; }
            5.   仔細(xì)分析這兩個線程函數(shù), 您就會正確理解CEvent 類。線程WriteD執(zhí)行到 WaitForSingleObject(eventWriteD.m_hObject,INFINITE);處等待,直到事件eventWriteD為有信號該線程才往下執(zhí)行,因為eventWriteD對象是自動事件,則當(dāng)WaitForSingleObject()返回時,系統(tǒng)自動把eventWriteD對象重置為無信號狀態(tài)。
            6. 雙擊按鈕IDC_WRITEW,添加其響應(yīng)函數(shù):
              void CMultiThread9Dlg::OnWritew()
                  {
                  CWinThread *pWriteW=AfxBeginThread(WriteW,
                  &m_ctrlW,
                  THREAD_PRIORITY_NORMAL,
                  0,
                  CREATE_SUSPENDED);
                  pWriteW->ResumeThread();
                  CWinThread *pWriteD=AfxBeginThread(WriteD,
                  &m_ctrlD,
                  THREAD_PRIORITY_NORMAL,
                  0,
                  CREATE_SUSPENDED);
                  pWriteD->ResumeThread();
                  }
              編譯并運行程序,單擊“寫‘W’”按鈕,體會事件對象的作用。

            C、使用CMutex 類

              互斥對象與臨界區(qū)對象很像.互斥對象與臨界區(qū)對象的不同在于:互斥對象可以在進(jìn)程間使用,而臨界區(qū)對象只能在同一進(jìn)程的各線程間使用。當(dāng)然,互斥對象也可以用于同一進(jìn)程的各個線程間,但是在這種情況下,使用臨界區(qū)會更節(jié)省系統(tǒng)資源,更有效率。

            D、使用CSemaphore 類

              當(dāng)需要一個計數(shù)器來限制可以使用某個線程的數(shù)目時,可以使用“信號量”對象。CSemaphore 類的對象保存了對當(dāng)前訪問某一指定資源的線程的計數(shù)值,該計數(shù)值是當(dāng)前還可以使用該資源的線程的數(shù)目。如果這個計數(shù)達(dá)到了零,則所有對這個CSemaphore 類對象所控制的資源的訪問嘗試都被放入到一個隊列中等待,直到超時或計數(shù)值不為零時為止。一個線程被釋放已訪問了被保護(hù)的資源時,計數(shù)值減1;一個線程完成了對被控共享資源的訪問時,計數(shù)值增1。這個被CSemaphore 類對象所控制的資源可以同時接受訪問的最大線程數(shù)在該對象的構(gòu)建函數(shù)中指定。

            CSemaphore 類的構(gòu)造函數(shù)原型及參數(shù)說明如下:

            CSemaphore (LONG lInitialCount=1,
            LONG lMaxCount=1,
            LPCTSTR pstrName=NULL,
            LPSECURITY_ATTRIBUTES lpsaAttributes=NULL);
            
            • lInitialCount:信號量對象的初始計數(shù)值,即可訪問線程數(shù)目的初始值;
            • lMaxCount:信號量對象計數(shù)值的最大值,該參數(shù)決定了同一時刻可訪問由信號量保護(hù)的資源的線程最大數(shù)目;
            • 后兩個參數(shù)在同一進(jìn)程中使用一般為NULL,不作過多討論;

              在用CSemaphore 類的構(gòu)造函數(shù)創(chuàng)建信號量對象時要同時指出允許的最大資源計數(shù)和當(dāng)前可用資源計數(shù)。一般是將當(dāng)前可用資源計數(shù)設(shè)置為最大資源計數(shù),每增加一個線程對共享資源的訪問,當(dāng)前可用資源計數(shù)就會減1,只要當(dāng)前可用資源計數(shù)是大于0的,就可以發(fā)出信號量信號。但是當(dāng)前可用計數(shù)減小到0時,則說明當(dāng)前占用資源的線程數(shù)已經(jīng)達(dá)到了所允許的最大數(shù)目,不能再允許其它線程的進(jìn)入,此時的信號量信號將無法發(fā)出。線程在處理完共享資源后,應(yīng)在離開的同時通過ReleaseSemaphore()函數(shù)將當(dāng)前可用資源數(shù)加1。

            下面給出一個簡單實例來說明 CSemaphore 類的用法。

            例程10 MultiThread10

            1. 建立一個基于對話框的工程MultiThread10,在對話框IDD_MULTITHREAD10_DIALOG中加入一個按鈕和三個編輯框控件,按鈕的ID為IDC_START,標(biāo)題為“同時寫‘A’、‘B’、‘C’”;三個編輯框的ID分別為IDC_A、IDC_B和IDC_C,屬性都選中Read-only;
            2. 在MultiThread10Dlg.h文件中聲明兩個線程函數(shù):
              UINT WriteA(LPVOID pParam);
                  UINT WriteB(LPVOID pParam);
                  UINT WriteC(LPVOID pParam); 
            3. 使用ClassWizard分別給IDC_A、IDC_B和IDC_C添加CEdit類變量m_ctrlA、m_ctrlB和m_ctrlC;
            4. 在MultiThread10Dlg.cpp文件中添加如下內(nèi)容:

              為了文件中能夠正確使用同步類,在文件開頭添加:

              #include "afxmt.h"
                  
              定義信號量對象和一個字符數(shù)組,為了能夠在不同線程間使用,定義為全局變量:
              CSemaphore semaphoreWrite(2,2); //資源最多訪問線程2個,當(dāng)前可訪問線程數(shù)2個,應(yīng)該改為(1,1)才能實現(xiàn)同步的效果
                  char g_Array[10]; 

              添加三個線程函數(shù):

              UINT WriteA(LPVOID pParam)
                  {
                  CEdit *pEdit=(CEdit*)pParam;
                  pEdit->SetWindowText("");
                  WaitForSingleObject(semaphoreWrite.m_hObject,INFINITE);
                  CString str;
                  for(int i=0;i<10;i++)
                  {
                  pEdit->GetWindowText(str);
                  g_Array[i]=''A'';
                  str=str+g_Array[i];
                  pEdit->SetWindowText(str);
                  Sleep(1000);
                  }
                  ReleaseSemaphore(semaphoreWrite.m_hObject,1,NULL);
                  return 0;
                  }
                  UINT WriteB(LPVOID pParam)
                  {
                  CEdit *pEdit=(CEdit*)pParam;
                  pEdit->SetWindowText("");
                  WaitForSingleObject(semaphoreWrite.m_hObject,INFINITE);
                  CString str;
                  for(int i=0;i<10;i++)
                  {
                  pEdit->GetWindowText(str);
                  g_Array[i]=''B'';
                  str=str+g_Array[i];
                  pEdit->SetWindowText(str);
                  Sleep(1000);
                  }
                  ReleaseSemaphore(semaphoreWrite.m_hObject,1,NULL);
                  return 0;
                  }
                  UINT WriteC(LPVOID pParam)
                  {
                  CEdit *pEdit=(CEdit*)pParam;
                  pEdit->SetWindowText("");
                  WaitForSingleObject(semaphoreWrite.m_hObject,INFINITE);
                  for(int i=0;i<10;i++)
                  {
                  g_Array[i]=''C'';
                  pEdit->SetWindowText(g_Array);
                  Sleep(1000);
                  }
                  ReleaseSemaphore(semaphoreWrite.m_hObject,1,NULL);
                  return 0;
                  }
                  
              這三個線程函數(shù)不再多說。在信號量對象有信號的狀態(tài)下,線程執(zhí)行到WaitForSingleObject語句處繼續(xù)執(zhí)行,同時可用線程數(shù)減1;若線程執(zhí)行到WaitForSingleObject語句時信號量對象無信號,線程就在這里等待,直到信號量對象有信號線程才往下執(zhí)行。
            5. 雙擊按鈕IDC_START,添加其響應(yīng)函數(shù):
              void CMultiThread10Dlg::OnStart()
                  {
                  CWinThread *pWriteA=AfxBeginThread(WriteA,
                  &m_ctrlA,
                  THREAD_PRIORITY_NORMAL,
                  0,
                  CREATE_SUSPENDED);
                  pWriteA->ResumeThread();
                  CWinThread *pWriteB=AfxBeginThread(WriteB,
                  &m_ctrlB,
                  THREAD_PRIORITY_NORMAL,
                  0,
                  CREATE_SUSPENDED);
                  pWriteB->ResumeThread();
                  CWinThread *pWriteC=AfxBeginThread(WriteC,
                  &m_ctrlC,
                  THREAD_PRIORITY_NORMAL,
                  0,
                  CREATE_SUSPENDED);
                  pWriteC->ResumeThread();
                  }
                  

            好吧,多線程編程就介紹到這里,希望本文能對您有所幫助。

            posted on 2011-04-25 17:31 厚積薄發(fā) 閱讀(160) 評論(0)  編輯 收藏 引用 所屬分類: Windows編程

            導(dǎo)航

            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            統(tǒng)計

            常用鏈接

            留言簿

            隨筆分類

            文章分類

            文章檔案

            搜索

            最新評論

            久久夜色精品国产亚洲| 久久www免费人成看片| 久久精品成人免费网站| 久久青青草原国产精品免费| 国产精品无码久久久久| 大香伊人久久精品一区二区 | 色8久久人人97超碰香蕉987| 国产产无码乱码精品久久鸭| 久久99精品综合国产首页| 久久涩综合| 成人妇女免费播放久久久| 久久久91人妻无码精品蜜桃HD| 精品久久久久久国产| 激情久久久久久久久久| 亚洲综合伊人久久综合| 久久精品国产只有精品66| 久久天堂AV综合合色蜜桃网| 久久国产乱子伦精品免费午夜| 久久九九兔免费精品6| 久久精品国产亚洲Aⅴ蜜臀色欲| 亚洲综合伊人久久大杳蕉| 久久久久亚洲精品中文字幕| A狠狠久久蜜臀婷色中文网| 久久99热这里只有精品66| 久久99精品久久久久久野外| MM131亚洲国产美女久久| 精品国产VA久久久久久久冰| 无码任你躁久久久久久| 久久se精品一区二区影院 | 久久婷婷色综合一区二区| 久久精品国产亚洲AV麻豆网站| 亚洲精品国产自在久久| 精品久久久久中文字| 色噜噜狠狠先锋影音久久| 久久99国产精品久久久| www性久久久com| 久久91精品国产91久久户| 91久久婷婷国产综合精品青草| 91精品国产高清久久久久久io| 久久久久亚洲精品天堂| 99re久久精品国产首页2020|