日本搞逼视频_黄色一级片免费在线观看_色99久久_性明星video另类hd_欧美77_综合在线视频

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > php教程 > Windows下多線程編程(二)

Windows下多線程編程(二)

來源:程序員人生   發布時間:2016-07-26 13:34:25 閱讀次數:2435次

線程的分類

1.     有消息循環線程

  •  MFC中有用戶界面線程,從CWinThread派生出1個新的類作為UI線程類CUIThread,然后調用AfxBeginthread(RUNTIME_CLASS(CUIThread));啟動線程。UI線程可以直接創建模態對話框,而不用擔心消息循環的問題,由于UI線程默許自帶消息循環。
  •  MFC非用戶界面線程,不能創建模態對話框,但是可以創建非模態對話框或普通窗口,但是必須自己寫消息循環。

  

復制代碼
MSG msg; while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
復制代碼

 

2.     無消息循環線程

  • MFC中的工作者線程
  • 其他沒有加消息循環的普通線程。

 

線程間的通訊

1.   同享內存變量

l  由于線程是同享進程內存的,所以通過全局/靜態變量來進行通訊效力最最高的。參數需要斟酌是不是加volitile。

l  通過傳遞的參數,如援用和指針。參數需要斟酌是不是加volitile。

2.   消息通知

  • 如果是子線程向主線程通訊,由于主線程有消息循環,所以子線程可以通過發送消息來向主線程通訊。通過消息通訊能夠避免使用全局變量帶來的耦合性。

SendMessage必須等待消息函數處理完成才返回,PostMessage則直接將消息放入消息隊列立即返回。所以SendMessage的消息參數可以是臨時變量,而PostMessage的消息參數必須保證足夠的生存周期。

  • 如果子線程有自定義的消息循環,也能夠通過PostThreadMessage來指定線程通訊。

       

復制代碼
while(true) { if(GetMessage(&msg,0,0,0)) //get msgfrom message queue { switch(msg.message) { case MY_MSG: // Todo: break; } } };
復制代碼

 

3.   其他方式

  • 所有跨進程的通訊方式,固然可以用于跨線程了。

 

線程之間的狀態

1.   異步

即多個線程彼此獨立,不受外部線程的影響。線程本身就是實現異步的1種方式。

2.   同步

即多個線程彼此依賴,線程A的計算結果是線程B的計算的條件,也就是說在開始線程B的計算之前必須等待線程A的計算完。

3.   互斥

即多個線程在操作同1個資源時,1個線程必須等另外一個線程結束了才能繼續操作。互斥與同步不同的地方是,互斥沒有前后關系。同1個資源,可以指全局變量,也能夠指1個文件對象或是其他的內核對象。由于內核對象是跨進程的,所以更是跨線程的。

            等待函數

1.    概念

WaitForSingleObject函數是等待內核對象從無信號狀態到有信號狀態或是超時即返回。也即無信號狀態時等待,有信號或超時立即返回。

WaitForMulitpleObjects函數是等待多個內核對象從無信號狀態到有信號狀態或是超時即返回(可以指明是所有對象或是任1對象)。

Windows具有幾種內核對象可以處于已通知狀態和未通知狀態:進程、線程、作業、文件、控制臺輸入/輸出/毛病流、事件、等待定時器、信號量、互斥對象。

2.    等待函數與內核對象之間的關系

對象

無信號狀態

有信號狀態

成功等待副作用

進程

進程活動時

進程終止時

線程

線程活動時

線程終止時

文件

I/O要求正在處理時

I/O要求結束時

控制臺輸入

不存在任何輸入

存在輸入時

文件修改通知

沒有任何文件修改通知

文件系統發現修改時

重置通知

自動重置事件

ResetEvent, PulseEvent或等待成功

當調用SetEvent或PulseEvnet時

重置事件

人工重置事件

ResetEvent,或PulseEvent

當調用SetEvent或PulseEvnet時

自動重置定時器

CancelWaitableTimer或等待成功

當時間到時(SetWaitableTimer)

重置定時器

人工重置定時器

CancelWaitableTimer

當時間到時(SetWaitableTimer)

信號量

等待成功

當資源數量>0時(ReleaseSemaphore)

數量減1

互斥量

等待成功

當未被線程具有時(ReleaseMutex)

獲得線程所有權

l 線程和進程創建及運行時都是無信號狀態,當結束運行時變成有信號狀態。

l 自動重置的事件(FALSE)對象,當等待成功的時候,會被修改成無信號狀態。

l 信號量對象,當調用ReleaseSemaphore(數量加1),處于有信號狀態,WaitForSingleObject會被觸發并且立行將信號數量減1.

 

 

        用戶模式與內核模式的優缺點

1.   用戶模式

優點:線程同步機制速度快

缺點:容易墮入死鎖狀態多個進程之間的線程同步會出現問題。(比如競爭資源、死鎖)

2.   內核模式

優點:支持多個進程之間的線程同步,避免死鎖

缺點:線程同步機制速度慢,線程必須從用戶模式轉為內核模式。這個轉換需要很大的代價:來回1次需要占用x 8 6平臺上的大約1 0 0 0個C P U周期。

 

 

線程間的狀態處理

1.   線程的異步

由于線程本身就是異步的。

2.   線程的同步

線程的同步主要是通過事件(Event)內核對象、信號量(Semaphore)內核對象和互斥量(Mutex)內核對象。由于都是內核對象,所以不但可以跨線程操作,還可以跨進程同步。

1.      線程的同步

線程的同步主要是通過事件(Event)內核對象、信號量(Semaphore)內核對象和互斥量(Mutex)內核對象。由于都是內核對象,所以不但可以跨線程操作,還可以跨進程同步。

事件(Event)內核對象

事件分兩種類型:人工重置事件和自動重置事件,前者在觸發WaitForSingleObject以后需要手動調用ResetEvent將事件設置為無信號;而后者在觸發WaitForSingleObject以后自動將事件設置為無信號狀態。

經常使用函數:

CreateEvent,創建事件對象。

OpenEvent,打開已創建的事件對象,可以跨進程打開。

SetEvent,將事件對象設置為有信號狀態。

ResetEvent,將事件對象設置為無信號狀態。

PulseEvent,將事件對象設置為有信號狀態,然后又設置為無信號狀態,此函數不經常使用。

復制代碼
HANDELg_hEvent; int Main() { g_hEvent =CreateEvent(NULL, TRUE, FALSE, NULL); _beginthreadex(NULL,0, ThreadFun1, 0); _beginthreadex(NULL,0, ThreadFun2, 0); SetEvnet(g_hEvent);// } DWORD WINAPIThreadFun1(PVOID pParam) { WaitForSingleObject(g_hEvent); // Todo... SetEvent(g_hEvnet); return 0; } DWORD WINAPIThreadFun2(PVOID pParam) { WaitForSingleObject(g_hEvent); // Todo... SetEvent(g_hEvnet); return 0; }
復制代碼

 

注意:如果上面創建的是人工重置事件,則兩個線程函數都將履行。如果是自動重置事件,則只能履行1個線程,且不能保證哪個線程先履行。如果要保證1個線程先履行,可以添加事件對象用來確保指定線程已履行,不能通過代碼的前后順序確保線程已履行。

2.      信號量(Semaphore)內核對象

信號量的使用規則:

當前信號量資源數大于0,則標記為有信號狀態。

當前信號量資源數為0,則標記為無信號狀態。

信號量資源數不能為負,且最大不能超過指定數量。

經常使用函數:

CreateSemaphore,創建信號量對象。

OpenSemaphore,打開指定信號量對象,可以跨進程。

ReleaseSemaphoer,資源計算加1。

復制代碼
HANDELg_hSema[2]; int Main() { g_hSema[0] =CreateSemaphore(NULL, 1, 1, NULL); g_hSema[1] =CreateSemaphore(NULL, 0, 1, NULL); _beginthreadex(NULL,0, ThreadFun1, 0); _beginthreadex(NULL,0, ThreadFun2, 0); } DWORD WINAPIThreadFun1(PVOID pParam) { WaitForSingleObject(g_hSema[0]); // Todo... ReleaseSemaphoer(g_hSema[1]); return 0; } DWORD WINAPIThreadFun2(PVOID pParam) { WaitForSingleObject(g_hSema[1]); // Todo... ReleaseSemaphoer(g_hSema[0]); return 0; }
復制代碼

 

這樣就可以夠保證ThreadFun1履行完了,再履行ThreadFun2,然后再履行ThreadFun1,并且保證每一個線程函數只能被調用1次.

3.      互斥量(Mutex)內核對象

互斥量內核對象確保線程具有單個資源的互斥訪問權。在行動特性上,互斥量與臨界區的1樣。只不過,互斥量是內核對象,使用時需要從用戶模式切換到內核模式,比較耗時。但正由于是內核對象,所以互斥量能夠跨進程,并且能夠設置超時時間,這是它比臨界區靈活的地方。

經常使用函數:

CreateMutex,創建互斥量對象。

OpenMutex,打開指定互斥量對象,可以跨進程。

ReleaseMutex,釋放互斥量,對象被標記為有信號狀態,觸發WaitForSingleObject。

互斥量和臨界區1樣,具有1個線程具有權的概念,即當前互斥量和當前臨界區的釋放只能由當前線程釋放,其他線程釋放無效。由于互斥量是內核對象,如果線程已終止,但是其所屬的互斥量仍然沒有釋放,內核管理器會自動釋放。臨界區沒有這個功能,由于臨界區不是內核對象,所以臨界區如果沒有正確釋放會致使死鎖。

HANDLECreateMutex(  LPSECURITY_ATTRIBUTESlpMutexAttributes,

  BOOL bInitialOwner,  LPCTSTR lpName);

bInitialOwner標記是不是由創建線程具有線程所有權,TRUE表示創建者具有,FALSE表示創建者不具有,則是第1個調用WaitForSingleObject的線程將取得線程所有權。

復制代碼
HANDELg_hMutex; int Main() { g_hMutex =CreateMutex(NULL,FALSE); _beginthreadex(NULL,0, ThreadFun1, 0); _beginthreadex(NULL,0, ThreadFun2, 0); } DWORD WINAPIThreadFun1(PVOID pParam) { WaitForSingleObject(g_hMutex); // Todo... ReleaseMutex(g_hMutex); return 0; } DWORD WINAPIThreadFun2(PVOID pParam) { WaitForSingleObject(g_hMutex); // Todo... ReleaseMutex(g_hMutex); return 0; }
復制代碼

 

兩個函數誰先調用,誰即獲得線程所有權。如果想指定線程先運行,需要判斷指定線程已履行以后再創建新線程,不能依托線程的代碼創建前后順序。

3.   線程的互斥

像互斥量對象一樣可以到達互斥的效果,只是互斥量功能更豐富,并且如果是簡單的資源互斥,使用臨界區的效力更優。

臨界區(Critical Section)是1段供線程獨占式訪問的代碼,也就是說若有1線程正在訪問該代碼段,其它線程想要訪問,只能等待當前線程離開該代碼段方可進入,這樣保證了線程安全。他工作于用戶級(相對內核級),在Window系統中CRITICAL_SECTION實現臨界區相干機制。

經常使用函數:

voidInitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection)  // 初始化臨界區

voidEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection)       // 進入臨界區

voidLeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection)       // 離開臨界區

voidDeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection)      // 釋放臨界區資源

由于臨界區具有線程所有權這個概念,即進入臨界區的線程才有權釋放臨界區。由于必須當前線程進入和釋放,更多的時候,臨界區是在1個函數里使用,為了確保不會由于中間退出函數致使沒有釋放,我們可以用以下方式來確保釋放。

復制代碼
class Mutex { public: Mutex() {InitializeCriticalSection(section); } ~Mutex() { DeleteCriticalSection(section);} void Enter() {EnterCriticalSection(section); } void Leave() {LeaveCriticalSection(section); } struct Lock; protected: Mutex(const Mutex&); Mutex& operator=(const Mutex&); CRITICAL_SECTION section; }; structMutex::Lock { Mutex& s; Lock(Mutex& s) : s(s) { s.Enter(); } ~Lock() { s.Leave(); } }; DWORD WINAPIThreadFun(PVOID pParam) { Mutex::Locklock(mutex); // Todo... return 0; }
復制代碼

 

注意

1.      注意所有內核對象在結束時都需要調用closeHandle()。

2.      跨線程調用MFC對象函數都是不安全的。由于MFC對象的1些函數都與TLS有關聯,  所以有些調用會出錯。如UpdateData(),最好通過句柄發消息來完成相應的功能。

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 57pao国产精品一区 | 一级黄色在线播放 | 免费国产视频在线观看 | 欧美色人| 欧美在线视频一区 | 高清国产一区二区三区 | 免费a级毛片在线观看 | 免费a视频 | 在线观看国产一区 | 另类 欧美 日韩 国产 在线 | 91精品国产乱码久久久久久久久 | 福利一区二区 | 五月综合激情 | 波多野结衣精品视频 | 欧美成人a | 亚洲欧洲一级片 | 欧美成人三区 | 欧美精品网 | 成人区精品一区二区 | 一个色影院| 国产一区二区毛片 | 亚洲小视频 | 麻豆少妇 | 色综合久久久久久久久久久 | 欧美成人视屏 | 成人国产精品久久 | 亚洲精品视频二区 | 成人精品一区二区户外勾搭野战 | 欧美不卡一区二区三区 | 九九视频网 | 国产一区二区三区免费在线 | 国产香蕉视频在线播放 | 在线国产视频 | 麻豆国产 | 免费日韩av在线 | 久久精品久久久久久 | 国产一级免费 | 国产精品久久国产精品 | 精品国产一区二区三区四区四 | 亚洲精品在线观看网站 | 变态 另类 欧美 大码 日韩 |