人們吃飯的時候總是這樣的:一邊吃一邊喝一邊呼吸。如果我是創建人類的大自然,我肯定會選擇搶先式多任務。
如果蓋房,這需要打地基、和水泥、砌牆,那就得找你個幫手了。作爲主線程的我就叫了幾個幫手做我的子線程直到把房子蓋好。
二、線程和進程有什麼不同?
在Windows中,進程是對象和句柄的巨大熔池,飽含所有資源卻什麼都不會做。而在UNIX中,內核黑客們爲進程綁了一個主線程,即進程除了是資源集合外,天生是可以運行的。
線程則是這巨大池子中的魚,往返穿梭、自由且忙碌,呼吸着、左右着這些資源。
三、Context switch(上下文轉換)的發生。
對同一對象操作是發生context switch是有風險的, 我們稱之爲race condition 競爭條件。
四、Atomic Operations 原子操作應運而生。
用標誌位來隔離多線程也是不科學的,會生產N多行代碼,發生context switch機會很大。原子性應發生在機器碼—彙編的級別上。所以我們需要一個不被操作系統中斷的標誌位操作。
五、線程間如何通信?
讓很多人同時在廚房幫你炒幾道菜,通常不是很順利。讓多線程同時處理同一件事情值得我們小心謹慎。
六、HANDLE內核對象需要釋放。
七、線程內核對象與線程的不同?
八、多線程的成功:
保證與主線程有最小的接觸面積。
各線程間的數據要相互分離,避免使用全局變量,避免共同使用GDI對象。
讓主線程處理UI界面。
九、線程的等待:不要在Win32中使用GetExitThread() ,會對系統資源造成嚴重的衝擊。
我們可以用waitSingleObject替代。這兩個函數都在線程核心對象被激發時返回。阻塞等待比每秒訪問幾百萬次某個函數要好得多。
十、什麼是一個被激發的對象?當線程運行時,線程核心對象未激發;當線程結束時,線程核心對象被激發。
十一、 GetExitCodeThread()獲得一個也終止線程的ID
十二、 waitForMultipleObject();同時等待多個監控多個內核對象的激發。
十三、 關於內核對象:
進程在被初始化時,系統爲其分配一個句柄表,此句柄表只用於內核對象。 句柄表的每一項記錄了每個內核對象的信息,所謂信息主要包括:(句柄在句柄表表中的)索引、指向內核對象內存的地址、訪問屏蔽標誌、內核對象句柄繼承標誌。
進程在初始化時句柄表是空的,每當進程創建一個內核對象時,系統就在該進程的句柄
表中找出一個空項,設置新創建的內核對象的信息。 內核對象的創建函數幾乎都是形如Cerate*的形式,這些函數返回新創建的內核對象的句柄。這裏返回的所謂句柄,其值實際上是該內核對象在進程句柄表中的索引,用於標識該對象在進程句柄表中的位置。
Cerate*函數返回失敗的話,返回值通常爲NULL(0),但也有一些是INVALID_HANDLE_VALUE(-1)。關閉內核對象通過調用CloseHandle完成,每調用一次,內核對象使用計數遞減1。
十四、 msgWaitForMultipleObjects();等待內核對象或消息的激發。
十五、 不要長期鎖定一個資源 。
十六、 在critical section(臨界區) 中如果線程down掉,我們無法取消這個section,如果需要這種機制可使用mutex,我們在監視線程退出時可以進行取消。
十七、 死鎖。哲學家問題,應用Mutex可以解決。
十八、 Mutex(互斥器)上鎖是對內核對象進行操作,花費時間要比critical section長,
可以跨進程使用。
可指定等待時間。
十九、 哲學家問題,用WaitForMultipleObjects()可同時等待多隻筷子的到來。
二十、 Semaphore(信號量)可以針對一組相同個體資源,並設立一個最大計數。當計數用完之後,之後的線程就必須等待。可以具名,可被多進程訪問。
二十一、 信號量(semaphore)和互斥器(Mutex)在剛建立時都要將設法不要讓別的線程立即使用。如:信號量裏的create時設數量爲0,Mutex中設當前線程佔有Mutex。
二十二、 注意EVENT的遺失現象。Event主要用於同步,是核心對象,他的激活它是可控制的,比較方便,多用於IOCP。可具名,被其他進程訪問
第二節 新的線程控制
一、在一個線程中結束另一個線程。
TerminateThread(),會引起內存泄漏。比較危險。
二、Signal(信號):
三、優先級:GetPriorityClass() / SetPriorityClass(); 微調:GetThreadPriority()、SetThreadPriority()
第三節 overlap IO
一、可以同時讀寫文件的許多部分。
二、OVERLAPPED結構體保存了信息。
三、以文件HANDLE作爲激發機制,可用waitForSingleObject()/ GetoverlappedResult()來等待未完的異步事件完成。別切在OVERLAPPED中保存了所需要的信息。
四、在OVERLAPPED的末尾設置EVENT結構體,當異步事件完成時,操作系統可以激發次EVENT核心對象。並且採用手動,不讓操作系統自動激發它,導致競爭條件。
從而可實現在等待同一個文件的多個事件—好像文件被切割後用事件通知一樣。
五、重疊IO的限制如下:最多同時等待64個事件。在傳輸小於32k的數據是平均要比普通方法多花費15%的時間。
六、IOCP:極大的發掘CPU的速率,對多CPU的服務器很適合。
七、IOCP對線程數的控制。有時可能因爲客觀需要實際線程數大於CPU上最大線程數。
八、IOCP中如果執行線程因爲某事(如文件io)而掛起,則CPu將閒置,故應創建比CPU數多的線程數。
創建線程數 = 執行中線程數 + 被阻塞線程數 + CP上等待着的後備線程數
九、避免返回Completion Packets。不需要IOCP返回一個包。創建OVERLAPPED結構體,初始化一個manul-reset 手工重置的EVENT結構體,放在OVERLAPPED中,並將最後一位置1.------《WIN32多線程程序設計》P182。
十、因爲c/c++運行標準庫比多線程出現的早,所以直接用CreateThread()會有同步方面的錯誤。因此,高手們給我們提供了beginThreadEx()來解決這個問題。
十一、 排它鎖:在一個就結構體內部設立一個鎖變量,任何線程訪問時都要開鎖,迫使多線程的同步訪問變成順序訪問,帶來了安全,卻失去了多線程的優勢。
十二、 C++的_beginThreadEx()中的參數中要使用unsigned類型,可定義一個typeDefine來轉換。
十三、 用C++的成員函數實現線程時,相應函數因爲默認需要一個this指針,但操作系統不知道,扔了一個LRARAM進去。
解決的辦法,將相應成員函數設爲static。並將this作爲參數。
十四、 C++類中添加Critical Section。可用C++對Critical Section對象進行封裝。
十五、 C++錯誤處理,
十六、 MFC中以掛起狀態啓動線程。CWindThread(),AfxBeginThread()
十七、 過程:
a) 創建線程,設參數爲CREATE_SUSPENDED.
b) 設置CWinThread()中的m_bAutoDelet.防止創建失敗後自動DELETE線程句柄。可能會產生race condition錯誤。
c) 調用ResumeThread()激活線程。
十八、 不要再UI線程間共享UI對象。會很卡。
十九、 MFC中的每一步都要做到錯誤檢測