程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 線程間的同步概述

線程間的同步概述

編輯:C++入門知識

線程間的同步概述 1.前言 前面幾篇文章著重介紹了多線程的三種創建方式及多線程間的4種通信方式,並采用大量的實例演示,相信大家對線程的創建和使用有了一定的了解。若還不了解請復習下前面的文章,多動手寫代碼和調試,光看不練,假把式。   今天先請大家看看下面一個多線程程序,操作很簡單,就是創建9個線程,並輸出相應的線程編號(即報數)。主要代碼如下:   [cpp]   //聲明線程處理函數   <strong><span style="color:#ff0000;">unsigned __stdcall</span></strong>ThreadFunc( void* pArguments);//工作線程函數   HANDLE m_handle[9];//線程句柄列表   CListBox m_List; //數據列表控件   /////////////////////////////////////////////////   int g_nCount= 0;//這個是<strong><span style="color:#ff0000;">全局變量</span></strong>,用於線程報數(計數)   //演示開始:創建線程   void CThreadProblem1Dlg::OnBnClickedButton1()   {         // TODO: 在此添加控件通知處理程序代碼         GetDlgItem(IDC_BUTTON1)->EnableWindow(FALSE);         m_List.ResetContent();//清空列表         g_nCount = 0; //重置報數,         SetDlgItemInt(IDC_EDIT_NUM,++m_nNum); //顯示操作的次數         //創建多線程         for (int i=0;i<9;i++)         {              m_handle[i] = (HANDLE)<strong><span style="color:#ff0000;">_beginthreadex</span></strong>(NULL,0, ThreadFunc,&m_List,0, NULL);          }         //WaitForMultipleObjects(10, handle, TRUE, INFINITE);  //在此處等待退出,將發現程序假死了。所以采用線程的方式等待       _beginthreadex(NULL,0, WaitThread,<strong><span style="color:#ff0000;">this</span></strong>, 0, NULL);    //等待上述的個線程都退出   }   //工作線程函數   unsigned __stdcall ThreadFunc(void* pArguments)   {         Sleep(100);//相關處理         g_nCount++; //計數加         CListBox *pList= (CListBox*)pArguments;         CString str;         str.Format("  子線程ID號為%4d 報數為:%d",GetCurrentThreadId(),g_nCount);       pList->AddString(str);//輸出         Sleep(100);//相關處理         return 0;    }   //線程函數:等待個演示線程都退出再使能開始按鈕   unsigned __stdcall WaitThread(void* pArguments)   {         CThreadProblem1Dlg *pMainDlg= (CThreadProblem1Dlg *)pArguments;         <strong><span style="color:#ff0000;">WaitForMultipleObjects</span></strong>(9,pMainDlg->m_handle,TRUE, INFINITE);  //等待所有線程都結束         EnableWindow(GetDlgItem(AfxGetApp()->m_pMainWnd->m_hWnd,IDC_BUTTON1),TRUE);//使能開始按鈕         return 0;   }   當運行一次OnBnClickedButton1()函數,將顯示下面的結果:     你一看,沒錯呀,就應該是這樣的,沒有錯呀!多運行幾次也是這樣的。但我要肯定的告訴你,上面的程序是有嚴重的問題,而運行結果也欺騙了你。正是運行結果大大蒙騙了你的理智和大腦。你發現問題了嗎?(提示:不是報數順序的問題)www.2cto.com   正是該錯誤有隱蔽性,你很難從結果中發現問題,除非你運氣特別好,一運行就能重現問題,但作為程序員,你決不能僅靠運氣,不可能你每次的運氣都這麼好。   多運行幾次上面的程序,你有可能發現問題,現在我把該程序改進下,使其具有自動識別錯誤的智能,你一眼就能發現問題的。   改進點:添加結果檢測功能,若正常,其線程的報數應該為1-9,有可能順序有變,但總和為45=1+2+3+4+5+6+7+8+9。程序將一直循環到程序退出。若不等於45就退出循環,表示有問題,即讓程序一直運行,直到有錯誤為止。前面的OnBnClickedButton1()函數和ThreadFunc()函數保存不變,WaitThread()函數添加一個判斷語句,改進程序如下:   [cpp]   //線程函數:等待個演示線程都退出再使能開始按鈕   unsigned __stdcall WaitThread(void* pArguments)   {         CThreadProblem1Dlg *pMainDlg= (CThreadProblem1Dlg *)pArguments;         WaitForMultipleObjects(9, pMainDlg->m_handle, TRUE,INFINITE); //等待所有線程都結束         EnableWindow(GetDlgItem(AfxGetApp()->m_pMainWnd->m_hWnd,IDC_BUTTON1),TRUE);//使能開始按鈕        <span style="color:#ff0000;"><strong> if(pMainDlg->m_bAuto&& !pMainDlg->IsError())         {//若自動使能,則繼續下輪操作              pMainDlg->OnBnClickedButton1();         }</strong></span>         return 0;   }   //添加IsError()函數,用以判斷結果是否正確。   // 自動判斷每次運行結果是否正確   bool CThreadProblem1Dlg::IsError(void)   {         int nValue[9]={0};         int nResult = 0;         CString szText;         for(int i=0;i<9;i++)         {//得到各個線程的報數              m_List.GetText(i,szText);           szText = szText.Right(1);              nValue[i] = atoi(szText);              nResult += nValue[i];         }         //判斷是否有相同的值出現         if (nResult != <span style="color:#ff0000;"><strong>45</strong></span>)         {//有錯誤              return true;         }         return false;   }   再運行上面的程序,選中“自動判斷”,程序將很快不停的運行,但很快將又停下來,運行結果如下圖所示,有可能你的結果和我的不一樣,但類型差不多的。         現在你發現問題了嗎?對了,報數出現相同數了(見上圖出現兩個“2”)。   你可能要問,怎麼會這樣呢?   這就是多線程最容易出現的問題,也是多線程編程的難點和核心。再說說上面程序,創建了9個線程,這9個線程是同時運行的(即並行運行),它們都要修改變量全局g_nCount(g_nCount++;),就有可能兩個或多個線程同時讀取到g_nCount,而當前的g_nCount已經被其它線程修改,即輸出的不是線程當前的值。這和單線程的順序執行是有很大不同的。   那有什麼方法解決上面的問題嗎?當然有,這就是在江湖中大名鼎鼎的線程同步技術,而且系統提供了多種線程同步的技術/方法。   2.什麼是同步 “同步”不是指平常所說的兩件事情同時進行。它的目的是使多個線程之間協調工作,而且常常是避免兩個線程同時進行某些操作,比如同時訪問同一個共享資源。一般來說,同步是通過暫時將會發生沖突操作的某個線程暫停執行(稱為阻塞線程),然後等待不會沖突時再繼續執行。   3.需要同步的情況 3.1、多個線程同時訪問同一對象時 MFC對象在對象級不是線程安全的,只有在類級才是。如:兩個線程可以安全地使用兩個不同的CString對象,但同時使用同一個CString對象就可能產生問題。如果必須使用同一個對象,那麼應該采取適當的同步化措施。   3.2、多個線程之間需要協調運行 例如,如果第二個線程需要等待第一個線程完成到某一步時才能運行,那麼該線程應該暫時掛起以減少對CPU的占用時間,提高程序的執行效率。當第一個線程完成了相應的步驟後,應該發出某種信號來激活第二個線程。   4.Windows中的4種線程同步技術 4.1、Events(事件)——CEvent 作為標志在線程之間傳遞信號。簡單地說,類似一個布爾型變量的開關作用。   4.2、Critical Sections(臨界段)——CCriticalSection 在進程中作為關鍵字以獲得對共享資源的訪問   4.3、Mutexes(互斥量)——CMutex 與臨界段的工作方式相似,只是該對象可以用於多進程中的線程同步,而不是用於單進程中   4.4、Semaphores(信號量)——CSemaphore 在給定的限制條件下,允許多個進程同時訪問共享資源

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved