国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

關于mfc線程 的退出問題、同步問題

2019-11-11 04:30:38
字體:
來源:轉載
供稿:網友
AfxBeginThread()函數的返回值是CWinThread* 指針,但是這個指針不能直接使用,因為這個指針會自動銷毀。如果道友直接使用了這個指針,那么當在操作這個指針時,若已被mfc銷毀,那么訪問違規將會到來。至于返回值的使用請看,我寫的一個mfc程序片段。[html] view plain copyCString strName = _T("");  CWinThread* pThread = NULL;  UINT CBcgTestDlg::ThreadWorkFunc(LPVOID lPvoid)  {            for (int n = 0; n < 10000; n++)      {          strName = _T("http://blog.csdn.net/windows_nt");          strName = strName + _T("/n");          TRACE(strName);      }            return 0;  }      void CBcgTestDlg::OnOK()   {      for (int n = 0; n < 10000; ++n)      {          if (pThread)          {              WaitForSingleObject(pThread->m_hThread, INFINITE);              delete pThread;          }                    pThread = AfxBeginThread(ThreadWorkFunc, NULL, 0, CREATE_SUSPENDED, NULL);          if (pThread)          {              pThread->m_bAutoDelete = FALSE;              pThread->ResumeThread();          }      }  }    

//現在可以放心的使用返回值pThread了,但是要記得在使用結束后記得調用delete pThread,釋放資源(CWinThread類中的線程句柄會在析構函數中自動釋放)。 線程同步的方式主要有:臨界區、互斥區、事件、信號量四種方式。一、

接下來我主要講一下自己在學習windows核心編程中對于臨界區線程同步方式的使用。

臨界區線程同步在windows核心編程中被稱為關鍵段線程同步,以下統稱關鍵段關鍵段是一小段代碼,它在執行之前需要獨占對一些資源的訪問權。缺點:能且只能用在一個進程中的多線程同步。可能陷入死鎖,因為我們無法為進入關鍵段的線程設置最大等待時間。接下來我介紹一些關鍵段線程同步的使用先看一個事例代碼

[html] view plain copyint g_nSum = 0;  CRITICAL_SECTION g_cs;    DWord WINAPI FirstThread(PVOID pvParam)  {    EnterCriticalSection(&g_cs);    g_nSum = 0;    for (int n = 0; n < 10000; ++n)    {      g_nSum += n;    }    LeaveCriticalSection(&g_cs);    return g_nSum;  }    DWORD WINAPI SecondThread(PVOID pvParam)  {    EnterCriticalSection(&g_cs);    g_nSum = 0;    for (int n = 0; n < 10000; ++n)    {      g_nSum += n;    }    LeaveCriticalSection(&g_cs);    return g_nSum;  }  在使用關鍵段(CRITICAL_SECTION)時,只有兩個必要條件:1、想要訪問資源的線程必須知道用來保護資源的CRITICAL_SECTION對象地址。CRITICAL_SECTION對象可以作為全局對象來分配,也可以作為局部對象來分配,或者從堆中動態地分配。2、如何線程在試圖訪問被保護的資源之前,必須對CRITICAL_SECTION結構的內部成員進行初始化。關鍵段線程同步常用函數介紹[html] view plain copy//1、首先我們要分配一個CRITICAL_SECTION對象,并進行初始化(使用關鍵段同步的線程必須調用此函數)  void InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection )    //2、當知道線程將不再需要訪問共享資源時,我們應該調用下邊的函數來清理CRITICAL_SECTION結構  void DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection )    //3、在對保護的資源進行訪問之前,必須調用下面的函數  void EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection )  //可以對上邊的函數多次調用,表示調用線程被獲準訪問的次數    //4、也可以用下邊的函數代替EnterCriticalSection  BOOL TryEnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection )  //通過返回值判斷當前線程是否獲準訪問資源,線程永遠不會進入等待狀態,如果  //返回TRUE表示該線程獲準并正在訪問資源,離開時必須調用LeaveCriticalSection()    //5、在代碼完成對資源的訪問后,必須調用以下函數,釋放訪問權限  void LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection )  //轉載請注明文章來自:http://blog.csdn.net/windows_nt  以上訪問方式(EnterCriticalSection方式)可能會使調用線程切換到等待狀態,這意味著線程必須從用戶模式切換到內核模式,這個切換開銷非常大。為了提高關鍵段的性能,Microsoft把旋轉鎖合并到了關鍵段中。因此,當調用EnterCriticalSection的時候,它會用一個旋轉鎖不斷地循環,嘗試在一段時間內獲得對資源的訪問權,只有當嘗試失敗時,線程才會切換到內核模式并進入等待狀態。

[html] view plain copy//1、為了在使用關鍵段的時候同時使用旋轉鎖,我們必須調用下面的函數來初始化關鍵段  BOOL InitializeCriticalSectionAndSpinCount( LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount )  //第二個參數dwSpinCount表示我們希望旋轉鎖循環的次數。    //2、我們也可以調用下面的函數來改變關鍵段的旋轉次數  DWORD SetCriticalSectionSpinCount( LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount )  //如果主機只有一個處理器,函數會忽略dwSpinCount參數  

Slim讀/寫鎖

SRWLock允許我們區分那些想要讀取資源的值的線程(讀取者線程)和想要更新資源的值的線程(寫入者線程)。[html] view plain copy//1、首先我們要分配一個SRWLOCK對象并用下邊函數初始化它。  void InitializeSRWLock( __out PSRWLOCK SRWLock )    //2、請求對保護資源的獨占訪問權(寫權限)  void AcquireSRWLockExclusive( __inout PSRWLOCK SRWLock )    //3、完成對資源的更新后,應該解除對資源的鎖定  void ReleaseSRWLockExclusive( __inout PSRWLOCK SRWLock )    //4、對應的讀者線程函數如下  void AcquireSRWLockShared( __inout PSRWLOCK SRWLock )  void ReleaseSRWLockShared( __inout PSRWLOCK SRWLock )  與關鍵段相比,PSRWLOCK缺乏下面兩個特性1、不存在TryEnter(Share/Exclusive)SRWLock之類的函數,如果鎖已經被占用,那么調用會阻塞調用線程2、不能遞歸的獲得PSRWLOCK。也就是說,一個線程不能為了多次寫入資源而多次鎖定資源,如后再多次釋放對資源的鎖定。線程同步性能排序(由高到低)volatile讀取 -->volatile寫入-->Interlocked API(原子方式)-->SRWLock-->關鍵段-->內核對象二、互斥器(Mutexes)的用途和臨界區(critical section)的用途非常相似,如:一個時間內只能夠有一個線程擁有mutex,就好像同一時間內只能夠有一個線程進入同一個critical section一樣。但是mutex通過犧牲速度,提高了靈活性,功能變得更加強大了。雖然mutex和critical section做相同的事情,但它們的運作還是有差別的:1、鎖住一個未被擁有的mutex,比鎖住一個未被擁有的critical section需要花費幾乎100倍的時間。2、mutex可以跨進程使用。critical section則只能在同一個進程中使用。

3、等待一個mutex時,你可以指定“結束等待”的世間長度,但對于critical section則不行。

造成以上差別的主要原因是:mutex是內核對象,critical section非內核對象。

以下是mutex和critical section的相關函數比較:

臨界區互斥器
CRITICAL_SECTIONInitializeCriticalSection()CreateMutex()OpenMutex()
EnterCriticalSection()WaitForSingleObject()WaitForMultipleObjects()MsgWaitForMultipleObjects()
LeaveCriticalSection()ReleaseMutex()
DeleteCriticalSection()CloseHandle()
使用mutex時注意:

在一個適當的程序中,線程絕對不應該在它即將結束前還擁有一個mutex,因為這意味著線程沒有能夠適當地清除其資源。不幸的是,我們并不身處一個完美的世界,有時候,因為某種理由,線程可能沒有在結束前調用ReleaseMutex()。為了解決這個問題,mutex有一個非常重要的特性。這性質在各種同步機制中是獨一無二的,如果線程擁有一個mutex而在結束前沒有調用ReleaseMutex(),mutex不會被摧毀,取而代之的是,該mutex會被視為“未被擁有”以及“未被激發”,而下一個等待中的線程會被以WAIT_ABANDONED_0通知。無論線程是因為ExitThread()而結束,或是因當掉而結束,這種情況都存在。

任何時候只要你想鎖住超過一個以上的同步對象,你就有死鎖的潛在病因。如果總是在相同時間把所有對象都鎖住,問題可去矣。事例如下,存在潛在死鎖的可能:

[html] view plain copyvoid SwapLists(List* list1, List* list2)  {      EnterCriticalSection(list1->critical_sec);      EnterCriticalSection(list2->critical_sec);      //list1,list2數據交換      LeaveCriticalSection(list1->critical_sec);      LeaveCriticalSection(list2->critical_sec);  }  正確的做法:[html] view plain copyvoid SwapLists(List* list1, List* list2)  {      HANDLE arrHandles[2];      arrHandles[0] = list1->hMutex;      arrHandles[1] = list2->hMutex;      WaitForMultipleObjects(2, arrHandles, TRUE, INFINITE);      //list1,list2數據交換      ReleaseMutex(arrHandles[0]);      ReleaseMutex(arrHandles[1]);  }  三、

前邊講過了互斥器線程同步-----windows核心編程-互斥器(Mutexes),這章我來介紹一下信號量(semaphore)線程同步。

理論上說,mutex是semaphore的一種退化。如果你產生一個semaphore并令最大值為1,那就是一個mutex。也因此,mutex又常被稱為binary semaphore。如果某個線程擁有一個binary semaphore,那么就沒有其他線程能夠獲得其擁有權。但是在win32中,這兩種東西的擁有權的意義完全不同,所以它們不能夠交換使用,semaphore不像mutex,它并沒有所謂的“wait abandoned”狀態可以被其他線程偵測到。每當一個鎖定動作成功,semaphore的現值就會減1,你可以使用任何一種wait...()函數來要求鎖定一個semaphore。如果semaphore的現值不為0,wait...()函數會立刻返回,這和mutex很像,當沒有任何線程擁有mutex,wait...()函數會立刻返回。注意,如果鎖定成功,你也不會收到semaphore的擁有權。因為可以有一個以上的線程同時鎖定一個semaphore。所以談semaphore的擁有權并沒有太多實際意義。在semaphore身上并沒有所謂“獨占鎖定”這種事情。也因為沒有所有權的觀念,一個線程可以反復調用wait...()函數以產生新的鎖定。這和mutex絕不相同:擁有mutex的線程不論再調用多少次wait...()函數,也不會被阻塞住。與mutex不同的是,調用ReleaseSemaphore()的那個線程,并不一定就得是調用wait...()的那個線程。任何線程都可以在任何時間調用ReleaseSemaphore(),解除被任何線程鎖定的semaphore。

以下是我對三種同步方式中,常用到的函數的總結。

臨界區互斥器信號量
CRITICAL_SECTIONInitializeCriticalSection()CreateMutex()OpenMutex()CreateSemaphore
EnterCriticalSection()WaitForSingleObject()WaitForMultipleObjects()MsgWaitForMultipleObjects()WaitForSingleObject()WaitForMultipleObjects()MsgWaitForMultipleObjects()...
LeaveCriticalSection()ReleaseMutex()ReleaseSemaphore()
DeleteCriticalSection()CloseHandle()CloseHandle()

事件線程同步----- window核心編程-內核對象線程同步

四、

上一章講了關鍵字(臨界區)線程同步,使用關鍵字線程同步,我們很容易陷入死鎖的情形,這是因為我們無法為進入關鍵段指定一個最長等待時間。

本章將討論如何使用內核對象來對線程同步。我們也將看到,與用戶模式下的同步機制(關鍵段同步)相比,內核對象的用途要廣泛的多。實際上,內核對象唯一的缺點就是它們的性能。內核對象包括進程、線程以及作業,幾乎所有這些內核對象都可以用來進行同步。對線程同步來書,這些內核對象中的每一種要么處于觸發狀態,要么處于未觸發狀態。Microsoft為每種對象創建了一些規則,規定如何在這兩種狀態之間進行轉換。例如,進程內核對象在創建的時候總是處于未觸發狀態。當進程終止的時候,操作系統會自動使進程內核對象變成觸發狀態。當進程內核對象被觸發后,它將永遠保持這種狀態,再也不會變回到未觸發狀態。在進程內核對象的內部有一個布爾變量,當系統創建內核對象的時候會把這個變量的值初始化為false(未觸發)。當進程終止的時候,操作系統會自動把相應的內核對象中的這個布爾值設為true,表示該對象已經被觸發。

下邊講一些內核同步中用到的函數。等該函數使一個線程自愿進入等待狀態,直到指定的內核對象被觸發為止。DWORD waitForSingleObject(HANDLE hObject, DWORD dwMilliseconds);//hObject:內核對象句柄//dwMilliseconds等待時間ms為單位,INFINITE為一直等待,只到內核對象被觸發為止。DWORD WaitForMultipleObjects( DWORD nCount, CONST HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds );//waitForSingleObject和WaitForMultipleObjects相似,唯一的不同之處在于它允許調用線程同時檢查多個內核對象的觸發狀態

/*函數的返回值告訴調用方函數為什么它得以繼續運行。如果給bWaitAll傳的是FALSE,那么只要任何一個對象被觸發,函數就會立即返回。這時的返回值是WAIT_OBJECT_0和(WAIT_OBJECT_0+dwCount-1)之間的任何一個值。換句話說,如果返回值既不是WAIT_TIMEOUT,也不是WAIT_FAILED,那么我們應該把返回值減去WAIT_OBJECT_0。得到的數值是我們在第二個參數中傳的句柄數組的一個索引,用來告訴我們被觸發的是那個對象。*/

//下面的事例代碼可以更清晰的對此進行解釋[html] view plain copyHANDLE h[3];//我的博客:<a href="http://blog.csdn.net/windows_nt">http://blog.csdn.net/windows_nt</a>  h[0] = hPRocess1;  h[1] = hProcess2;  h[2] = hProcess3;  DWORD dw = WaitForMultipleObjects(3, h, FALSE, 5000);  switch(dw)  {  case WAIT_FAILED:      //Bad call to function (invalid handle)      break;  case WAIT_TIMEOUT:      //None of the objects became signaled within 5000 milliseconds      break;  case WAIT_OBJECT_0 + 0:      //h[0] signaled, hProcess1 terminated      break;  case WAIT_OBJECT_0 + 1:      //h[1] signaled, hProcess2 terminated      break;  case WAIT_OBJECT_0 + 2:      //h[2] signaled, hProcess3 terminated      break;  }  

2、事件內核對象

//事件包含一個使用計數(這一點和所有其他內核對象一樣),一個用來表示事件是自動重置事件還是手動重置事件的布爾值,以及另一個用來表示事件有沒有被觸發的布爾值。//有兩種不同類型的事件對象:手動重置事件和自動重置事件。當一個手動重置事件被觸發的時候,正在等待該事件的所有線程都將變成可調度狀態,而當一個自動重置事件被觸發的時候,只有一個正在等待該事件的線程會變成可調用狀態。//創建一個事件內核對象HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, //安全屬性BOOL bManualReset, //自動重置/手動重置BOOL bInitialState, //是否觸發

LPCSTR lpName );//事件內核對象的名字,可以為空

//新版本

HANDLE CreateEventEx(LPSECURITY_ATTRIBUTES psa, //安全屬性PCTSTR pszName, //名字DWORD dwFlags, //是否觸發

DWORD dwDesiredaccess)

//dwDesiredAccess:允許我們指定在創建事件時返回的句柄對事件有何種訪問權限。這是一種創建事件句柄的新方法,它可以減少權限,相比較而言,CreateEvent()總是被授予全部權限。但CreateEventEx()更有用的地方在于它允許我們以減少權限的方式來打開一個已經存在的事件,而CreateEvent()總是要求全部權限。

//下邊這個例子展示了如何使用事件內核對象來對線程進行同步。[html] view plain copy//Create a global handle to a manual-reset, nonsignaled event.  HANDLE g_hEvent;  int WINAPI _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,                        LPSTR lpCmdLine, int nShowCmd )  {      //Create the manual-reset, nonsignaled event      g_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);      //Spawn 3 new threads      HANDLE hThread[3];      DWORD dwThread;      hThread[0] = _beginthread(NULL, 0, wordCount, NULL, 0, &dwThread);      hThread[1] = _beginthread(NULL, 0, SpellCheck, NULL, 0, &dwThread);      hThread[2] = _beginthread(NULL, 0, GrammarCheck, NULL, 0, &dwThread);          OpenFileAndReadContentsIntoMemory(...);      //allow all 3 threads to access the memory      SetEvent(g_hEvent);  }      DWORD WINAPI wordCount(PVOID pvParam)  {      //Wait until the file's data is in memory      waitForSingleObject(g_hEvent, INFINITE);      //access the memory block.      return 0;  }      DWORD WINAPI SpellCheck(PVOID pvParam)  {      //Wait until the file's data is in memory      waitForSingleObject(g_hEvent, INFINITE);      //access the memory block.      return 0;  }      DWORD WINAPI GrammarCheck(PVOID pvParam)  {      //Wait until the file's data is in memory      waitForSingleObject(g_hEvent, INFINITE);      //access the memory block.      return 0;  }  

下邊是我自己寫的一個事例片段,很簡單

[html] view plain copyCString strName = _T("");    UINT CBcgTestDlg::ThreadWorkFunc(LPVOID lPvoid)  {            for (int n = 0; n < 10000; n++)      {          strName = _T("http://blog.csdn.net/windows_nt");          strName = strName + _T("/n");          TRACE(strName);      }            return 0;  }    void CBcgTestDlg::OnOK()   {      CWinThread* pThread = NULL;        for (int n = 0; n < 10000; ++n)      {          if (pThread)          {              WaitForSingleObject(pThread->m_hThread, INFINITE);              delete pThread;          }                    pThread = AfxBeginThread(ThreadWorkFunc, NULL, 0, CREATE_SUSPENDED, NULL);          if (pThread)          {              pThread->m_bAutoDelete = FALSE;              pThread->ResumeThread();          }      }  }  注意線程函數可以為類函數,但必須是靜態函數


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 板桥市| 兰考县| 云林县| 若尔盖县| 海南省| 宜川县| 寻乌县| 东光县| 大化| 龙井市| 宜良县| 高雄市| 平和县| 元江| 长兴县| 安吉县| 武清区| 山阴县| 新竹县| 内乡县| 保定市| 邹平县| 瓮安县| 安乡县| 长春市| 青岛市| 库伦旗| 永胜县| 潞城市| 新丰县| 刚察县| 剑川县| 乌什县| 周口市| 临清市| 绥芬河市| 小金县| 九台市| 永丰县| 大足县| 浦江县|