Windows多線程編程總結__編程

來源:互聯網
上載者:User
Windows多線程編程總結

關鍵字:多線程 線程同步 線程池 核心對象 1 核心對象 1 .1核心對象的概念

核心對象是核心分配的一個記憶體塊,這種記憶體塊是一個資料結構,表示核心對象的各種特徵。並且只能由核心來訪問。應用程式若需要訪問核心對象,需要通過作業系統提供的函數來進行,不能直接存取核心對象(Windows從安全性方面來考慮的)。

核心對象通過Create*來建立,返回一個用於標識核心對象的控制代碼,這些控制代碼(而不是核心對象)可在建立進程範圍內使用,不能夠被傳遞到其他進程中被使用。 1 .2核心對象使用的計數

因為核心對象的所有者是核心,而不是進程,所以何時撤銷核心對象由核心決定,而核心做這個決定的依據就是該核心對象是否仍然被使用。那麼如何判斷核心對象是否被使用呢。可以通過核心對象的“使用計數”屬性,一旦這個值變成0了,核心就可以釋放該對象了。 1 .3建立核心對象 1 .3.1進程與控制代碼表

每個進程在初始化的時候,將被分配一個控制代碼表,該控制代碼表中只儲存核心對象的控制代碼,不儲存使用者物件的控制代碼。控制代碼表的詳細結構微軟沒有公布,但是大致包含三個內容:核心物件控點,核心對象地址,訪問屏蔽標誌。

微軟為何要將核心對象的控制代碼設定為進程相關的呢。理由有:

l         不同的進程對核心對象的存取權限是不同的,有必要區分對待

l         如果控制代碼是全域的,則一個進程可以控制另外一個進程的控制代碼,破壞另外一個進程的控制代碼。

  1 .3.2建立核心對象及作業系統內部機制

利用CreateSomeObject的函數來建立核心對象。調用該函數的時候核心就為該對象分配一個記憶體塊,並進行初始化,然後核心再掃描該進程的控制代碼表,初始化一條記錄並放在控制代碼表中。 1 .3.3進程中使用核心對象的內部機制

假設函數F使用某個核心對象,其參數為Handle1,則該函數內部需要尋找該進程的控制代碼表,找出參數控制代碼對應的記錄,然後才能使用該核心對象。 1 .4關閉核心對象

無論進程怎樣建立核心對象,在不使用該對象的時候都應當通過Bool CloseHandle(HANDLE hobj)來向作業系統聲明結束對該對象的訪問。為什麼叫聲明呢。是因為此時也許還有其他進程對該對象的訪問,作業系統可能並不立即釋放該對象。作業系統需要做的是:從進程的控制代碼表中刪除該核心對象的記錄,另外再考察該核心對象的使用計數以決定是否需要釋放該對象。

1 .5核心對象的共用

說到共用,與之孿生的就是共用許可權。Windows核心對象的共用有三種方式: 1 .5.1繼承式共用(父子進程間)

只有當進程是父子關係的時候,才能使用此種方式的共用。特別要注意的是繼承的是核心對象的控制代碼,核心對象本身是不具備繼承性。要達到這種繼承的效果需要做以下幾件事:

l         在進程建立核心對象的時候,需要一個安全結構sa(SECURITY_ATTRIBUTES類型,以向OS聲明對象的訪問方式)作為參數。繼承式共用需要將結構的成員sa.bInheritHandle設定為TRUE。此時OS內部的處理式將進程的控制代碼表中的該對象的訪問屏蔽欄位設定成“可繼承”。

l         在建立子進程(CreateProcess函數)時,設定建立參數bInheritHandles為TRUE。表示被建立的子進程可以繼承父進程中的所有可繼承核心對象。OS內部的處理是:複製父進程控制代碼表中的記錄到子進程的控制代碼表中,並使用相同的控制代碼值;為核心對象的使用計數器加1。

特別說明:子進程能夠繼承的的核心對象僅局限於父進程建立它的時候所擁有的可繼承核心對象。子進程誕生後,父進程再搞出什麼可繼承的東西,子進程是不能用的。這就需要在子進程中使用繼承的核心對象的時候需要謹慎,以確定核心對象是否已被繼承了。

利用SetHandleinformation方法可以隨時修改核心物件控點的一些屬性,目前公開的控制代碼屬性有兩種,一種是該控制代碼是否能被繼承,另一種是該控制代碼是否能被關閉。 1 .5.2同名共用

同名共用,不需要共用進程之間存在父子關係。但局限於核心對象是否支援這種共用方式。建立核心對象的Create函數中是否包含pszName是該核心對象是否支援同名共用的標誌。

l         方法一:當Process1通過CreateObject(…”someName”)建立了一個名字為someName的核心對象後,Process2也調用了CreateObject(…”someName”),此時核心的動作是:在全域中查詢發現已經存在someName1的對象;為Process2的控制代碼表添加一條Ojbect的記錄,使用的控制代碼不確定;為someName這個Object的引用計數器加1。

l         方法二:Process2使用OpenObject(…”someName”)的方式來獲得對名someName的Object的控制代碼。用這種Open方法的時候,需要提供一個參數讓OS鑒權,以判定是否能夠以參數指定的方式來訪問核心對象。 1 .5.3複製核心對象的控制代碼的方式共用

跨進程邊界的核心對象共用的另外一個方法是通過DuplicateHandle來複製核心物件控點。

如果要將ProcessS中的對象拷貝到ProcessT中則調用DuplicateHandle的進程一定要有對這兩個進程的訪問權,即控制代碼表中擁有這兩個進程核心對象的控制代碼記錄。 2 線程的一般概念 2 .1視圖

l         進程只是線程的容器,從來不執行任何東西

l         線程總是在某個進程中被建立

l         線程在進程的地址空間中執行代碼

l         線程們共用進程中的所有核心對象 3 線程的建立

1
2
3
4
5
6
7
HANDLE CreateThread(
    PSECURITY_ATTRIBUTES psa,
    DWORD cbStack,
    PTHREAD_START_ROUTINE pfnStartAddr,
    PVOID pvParam,
    DWORD fdwCreate,
    PDWORD pdwThreadID);

《Windows核心編程》P124介紹說應當使用編譯器提供的線程建立函數,而不應當直接使用CreateThread。 3 .1CreateThread調用的核心行為

調用CreateThread後,OS進行如下幾個動作:

l         產生一個線程核心對象

l         在進程空間內為線程分配堆棧空間

因為線程的環境同於其所在進程的環境,所以建立的線程可以訪問進程中的所有資源,包括線程中所有的核心對象。 4 線程銷亡 4 .1終止線程的方式:

l         線程函數返回(最好使用這個方式,可以保證:線程種建立的C++對象正常析構;OS釋放線程堆棧記憶體;OS將線程的退出碼設定為線程函數的傳回值;系統將遞減該線程核心對象的的使用計數器【如果此時還有其他引用……,見下面說明】。)

l         調用ExitThread(不能釋放C++對象,所以最好不要使用這個方式。另外,如果非要調用也應當調用編譯器推薦的,如_endThread【Windows核心編程P127】)

l         同進程內的其他線程(包括主線程)調用TerminateThread(被撤銷線程得不到通知,不能釋放資源,盡量避免這種方式。另外這個函數是個非同步函數,返回時,線程不保證已經被撤銷,如果要觀察線程是否被撤銷,應當使用WaitForSingleObject)

l         包含線程的進程終止(應當避免這種方式) 4 .2線程退出時OS的行為

l         線程內的所有使用者物件被釋放。

l         線程的退出碼從STILL_ACTIVE改為傳遞給ExitThread或TerminateThread的代碼

l         線程核心對象的狀態改為“已通知”

l         如果線程為進程中的最後一個線程,則OS將進程當作已終止運行

l         線程核心對象的引用計數器減1(一旦線程終止了,其他引用改線程核心對象將不能夠處理改線程的控制代碼,但是可以通過調用GetExitcodeThread來檢查hThread代表的線程是否已經終止運行了。) 5 線程同步 5 .1線程同步的起因以及解決之道 5 .1.1共用資源型:多個線程需要訪問同一個資源的時候,為了保證資源不被破壞,需要線程對資源的訪問具有原子性。 5 .1.2依賴型:一個線程等待另外一個線程某件事情完成後才能執行_可以通過手動事件的方式互相通知。 5 .2線程同步種類細分

同步起因

同步種類

同步方法備忘

共用資源

多個線程對共用變數做加減操作

互鎖函數族之:InterlockedExchangeAdd

共用資源

多個線程對公開變數、指標做賦值操作

互鎖函數族之:InterlockedExchange ,InterlockedExchangepoint

共用資源

多個線程需要根據對公開變數、指標的判斷做操作選擇

互鎖函數族之:InterlockedCompareExchange

              InterlockedCompareExchangePointer

共用資源

複雜資料結構(非單值),不適合互鎖函數族處理的

用“關鍵代碼”的方式,關鍵代碼中要注意1、要盡量的快速處理完,以防止其他等待線程等待太長時間 2、線程等待過程中由使用者模式切換到核心模式,耗費1000個CPU周期,時間比較長。3、只能對單個進程中的線程進行同步)

InitializeCriticalSection;

EnterCriticalSection;

LeaveCriticalSection;

DeleteCriticalSection;

處理線程同步的一種方法

對線程同步做的一個抽象,線程的同步本質上都是依賴於某個其他事件的發生,用軟體的方法來對所依賴的事件做一個抽象,將有助與程式編寫的簡捷

CreateEvent

Event的重要屬性有一個是“自動”or“手動”,如果是自動的,則在某個線程用Wait××成功等待到事件的“通知”狀態後,則事件狀態立刻變成“未通知”狀態,以保證同時對資源訪問的線程只有一個。

原則上不算線程同步範疇,而屬於對wait**的一種應用方式

一個可以作為定時器的核心對象,Waitable Timer

CreateWaitableTimer

SetWaitableTimer,

CancelWaitableTimer

共用資源

一組線程對一組同樣性質的資源的爭用,則這組資源需要有所表示,以告知線程們是否有閒置給以為他們服務,以訊號量機制實現

CreateSemaphore,ReleaseSemaphore

共用資源

一組線程對一個單一的資源的爭用,需要有一種機制保證同一個事件只有一個線程能得到資源。以Mutex方式實現

CreateMutex

ReleaseMutex

與關鍵代碼的差別在於:

1、   允許不同進程的線程之間同步

2、   核心對象,使用者模式和核心模式切換的時候需要更多的CPU開銷

特別說明:WaitForSingleObject/WaitForMultipleObject是抑制線程本身的一種手法,配合以共用資來源物件或所依賴的其他對象“通知狀態”的原子性變化,以達到線程在爭用資源、互相依賴時執行的順序化,從而達到同步的目的。

綜上:其實Windows的線程同步機制是提供了一組不同情況下的資源爭用處理辦法而已。與此同時推出的Wait××卻可以帶來很多其他好處,甚至部分緩解C++語言沒有事件機制的缺憾,部分達到了Java,C#中事件機制的效果,為Oberserve模式的實現做了些貢獻。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.