多線程詳解

1. ? 進程與線程有那些區別和聯系?
  每個進程至少需要一個線程。
? ? ? ? 進程由兩部分構成:進程內核對象,地址空間。線程也由兩部分組成:線程內核對象,操作系統用它來對線程實施管理。線程堆棧,用于維護線程在執行代碼時需要的所有函數參數和局部變量。
? ? ? ? 進程是不活潑的。進程從來不執行任何東西,它只是線程的容器。線程總是在某個進程環境中創建的,而且它的整個壽命期都在該進程中。
? ? ? ? 如果在單進程環境中,有多個線程正在運行,那么這些線程將共享單個地址空間。這些線程能夠執行相同的代碼,對相同的數據進行操作。這些線程還能共享內核對象句柄,因為句柄表依賴于每個進程而不是每個線程存在。
  進程使用的系統資源比線程多得多。實際上,線程只有一個內核對象和一個堆棧,保留的記錄很少,因此需要很少的內存。因此始終都應該設法用增加線程來解決編程問題,避免創建新的進程。但是許多程序設計用多個進程來實現會更好些。

2. ? 如何使用_beginthreadex函數?
? ? ? ? 使用方法與CreateThread函數相同,只是調用參數類型需要轉換。

3. ? 如何使用CreateThread函數?
? ? ? ? 當CreateThread被調用時,系統創建一個線程內核對象。該線程內核對象不是線程本身,而是操作系統用來管理線程的較小的數據結構。使用時應當注意在不需要對線程內核進行訪問后調用CloseHandle函數關閉線程句柄。因為CreateThread函數中使用某些C/C++運行期庫函數時會有內存泄漏,所以應當盡量避免使用。
參數含義:
lpThreadAttributes ? ? 如果傳遞NULL該線程使用默認安全屬性。如果希望所有的子進程能夠繼承該線程對象的句柄,必須將它的bInheritHandle成員被初始化為TRUE。
dwStackSize ? ? 設定線程堆棧的地址空間。如果非0,函數將所有的存儲器保留并分配給線程的堆棧。如果是0,CreateThread就保留一個區域,并且將鏈接程序嵌入.exe文件的/STACK鏈接程序開關信息指明的存儲器容量分配給線程堆棧。
lpStartAddress ? ? 線程函數的地址。
lpParameter ? ? 傳遞給線程函數的參數。
dwCreationFlags ? ? 如果是0,線程創建后立即進行調度。如果是CREATE_SUSPENDED,系統對它進行初始化后暫停該線程的運行。
LpThreadId ? ? 用來存放系統分配給新線程的ID。

4. ? 如何終止線程的運行?
(1) ? ? ? 線程函數返回(最好使用這種方法)。
這是確保所有線程資源被正確地清除的唯一辦法。如果線程能夠返回,就可以確保下列事項的實現:
  在線程函數中創建的所有C++對象均將通過它們的撤消函數正確地撤消。操作系統將正確地釋放線程堆棧使用的內存。
系統將線程的退出代碼設置為線程函數的返回值。系統將遞減線程內核對象的使用計數。
(2) ? ? ? 調用ExitThread函數(最好不要使用這種方法)。
該函數將終止線程的運行,并導致操作系統清除該線程使用的所有操作系統資源。但是,C++資源(如C++類對象)將不被撤消。
(3) ? ? ? ? 調用TerminateThread函數(應該避免使用這種方法)。
TerminateThread能撤消任何線程。線程的內核對象的使用計數也被遞減。TerminateThread函數是異步運行的函數。如果要確切地知道該線程已經終止運行,必須調用WaitForSingleObject或者類似的函數。當使用返回或調用ExitThread的方法撤消線程時,該線程的內存堆棧也被撤消。但是,如果使用TerminateThread,那么在擁有線程的進程終止運行之前,系統不撤消該線程的堆棧。
(4) ? ? ? ? 包含線程的進程終止運行(應該避免使用這種方法)。由于整個進程已經被關閉,進程使用的所有資源肯定已被清除。就像從每個剩余的線程調用TerminateThread一樣。這意味著正確的應用程序清除沒有發生,即C++對象撤消函數沒有被調用,數據沒有轉至磁盤等等。一旦線程不再運行,系統中就沒有別的線程能夠處理該線程的句柄。然而別的線程可以調GetExitcodeThread來檢查由hThread標識的線程是否已經終止運行。如果它已經終止運行,則確定它的退出代碼。

5. ? 為什么不要使用_beginthread函數和_endthread函數?
  與_beginthreadex函數相比參數少,限制多。無法創建暫停的線程,無法取得線程ID。    Endthread函數無參數,線程退出代碼必須為0。還有_endthread函數內部關閉了線程的句柄,一旦退出將不能正確訪問線程句柄。

6. ? 如何對進程或線程的內核進行引用?
HANDLE ? GetCurrentProcess( ? ? );
HANDLE ? GetCurrentThread( ? ? );
這兩個函數都能返回調用線程的進程的偽句柄或線程內核對象的偽句柄。偽句柄只能在當前的進程或線程中使用,在其它線程或進程將不能訪問。函數并不在創建進程的句柄表中創建新句柄。調用這些函數對進程或線程內核對象的使用計數沒有任何影響。如果調用CloseHandle,將偽句柄作為參數來傳遞,那么CloseHandle就會忽略該函數的調用并返回FALSE。
DWORD ? GetCurrentProcessId( ? ? );
DWORD ? GetCurrentThreadId( ? ? );
這兩個函數使得線程能夠查詢它的進程的唯一ID或它自己的唯一ID。

7. ? 如何將偽句柄轉換為實句柄?
HANDLE ? hProcessFalse ? = ? NULL;
HANDLE ? hProcessTrue ? = ? NULL;
HANDLE ? hThreadFalse ? = ? NULL;
HANDLE ? hThreadTrue ? = ? NULL;

hProcessFalse ? = ? GetCurrentProcess( ? ? );
hThreadFalse ? = ? GetCurrentThread( ? ? );
取得線程實句柄:
DuplicateHandle( ? hProcessFalse, ? hThreadFalse, ? hProcessFalse, ? &hThreadTrue, ? 0, ? FALSE, ? DUPLICATE_SAME_ACCESS ? );
取得進程實句柄:
DuplicateHandle( ? hProcessFalse, ? hProcessFalse, ? hProcessFalse, ? &hProcessTrue, ? 0, ? FALSE, ? DUPLICATE_SAME_ACCESS ? );
由于DuplicateHandle會遞增特定對象的使用計數,因此當完成對復制對象句柄的使用時,應該將目標句柄傳遞給CloseHandle,從而遞減對象的使用計數。

8. ? 在一個進程中可創建線程的最大數是得多少?
線程的最大數取決于該系統的可用虛擬內存的大小。默認每個線程最多可擁有至多1MB大小的棧的空間。所以,至多可創建2028個線程。如果減少默認堆棧的大小,則可以創建更多的線程。

線程的調度、優先級和親緣性
9. ? 如何暫停和恢復線程的運行?
  ? ? 線程內核對象的內部有一個值指明線程的暫停計數。當調用CreateProcess或CreateThread函數時,就創建了線程的內核對象,并且它的暫停計數被初始化為1。因為線程的初始化需要時間,不能在系統做好充分的準備之前就開始執行線程。線程完全初始化好了之后,CreateProcess或CreateThread要查看是否已經傳遞了CREATE_SUSPENDED標志。如果已經傳遞了這個標志,那么這些函數就返回,同時新線程處于暫停狀態。如果尚未傳遞該標志,那么該函數將線程的暫停計數遞減為0。當線程的暫停計數是0的時候,除非線程正在等待其他某種事情的發生,否則該線程就處于可調度狀態。在暫停狀態中創建一個線程,就能夠在線程有機會執行任何代碼之前改變線程的運行環境(如優先級)。一旦改變了線程的環境,必須使線程成為可調度線程。方法如下:
hThread ? = ? CreatThread( ? ……,CREATE_SUSPENDED,…… ? );

bCreate ? = ? CreatProcess( ? ……,CREATE_SUSPENDED,……,pProcInfo ? );
if( ? bCreate ? != ? FALSE ? )
{
? hThread ? = ? pProcInfo.hThread;
}
……
……
……
ResumeThread( ? hThread ? );
CloseHandle( ? hThread ? );
ResumeThread成功,它將返回線程的前一個暫停計數,否則返回0xFFFFFFFF。

單個線程可以暫停若干次。如果一個線程暫停了3次,它必須恢復3次。創建線程時,除了使用CREATE_SUSPENDED外,也可以調用SuspendThread函數來暫停線程的運行。任何線程都可以調用該函數來暫停另一個線程的運行(只要擁有線程的句柄)。線程可以自行暫停運行,但是不能自行恢復運行。與ResumeThread一樣,SuspendThread返回的是線程的前一個暫停計數。線程暫停的最多次數可以是MAXIMUM_SUSPEND_COUNT次。SuspendThread與內核方式的執行是異步進行的,但是在線程恢復運行之前,不會發生用戶方式的執行。調用SuspendThread時必須小心,因為不知道暫停線程運行時它在進行什么操作。只有確切知道目標線程是什么(或者目標線程正在做什么),并且采取強有力的措施來避免因暫停線程的運行而帶來的問題或死鎖狀態,SuspendThread才是安全的。

10. ? 是否可以暫停和恢復進程的運行?
? ? ? ? 對于Windows來說,不存在暫停或恢復進程的概念,因為進程從來不會被安排獲得CPU時間。不過Windows確實允許一個進程暫停另一個進程中的所有線程的運行,但是從事暫停操作的進程必須是個調試程序。特別是,進程必須調用WaitForDebugEvent和ContinueDebugEvent之類的函數。由于競爭的原因,Windows沒有提供其他方法來暫停進程中所有線程的運行。

11. ? 如何使用sleep函數?
? ? ? ? 系統將在大約的指定毫秒數內使線程不可調度。Windows不是個實時操作系統。雖然線程可能在規定的時間被喚醒,但是它能否做到,取決于系統中還有什么操作正在進行。
  可以調用Sleep,并且為dwMilliseconds參數傳遞INFINITE。這將告訴系統永遠不要調度該線程。這不是一件值得去做的事情。最好是讓線程退出,并還原它的堆棧和內核對象。可以將0傳遞給Sleep。這將告訴系統,調用線程將釋放剩余的時間片,并迫使系統調度另一個線程。但是,系統可以對剛剛調用Sleep的線程重新調度。如果不存在多個擁有相同優先級的可調度線程,就會出現這種情況。

12. ? 如何轉換到另一個線程?
? ? ? ? 系統提供了SwitchToThread函數。當調用這個函數的時候,系統要查看是否存在一個迫切需要CPU時間的線程。如果沒有線程迫切需要CPU時間,SwitchToThread就會立即返回。如果存在一個迫切需要CPU時間的線程,SwitchToThread就對該線程進行調度(該線程的優先級可能低于調用SwitchToThread的線程)。這個迫切需要CPU時間的線程可以運行一個時間段,然后系統調度程序照常運行。該函數允許一個需要資源的線程強制另一個優先級較低、而目前卻擁有該資源的線程放棄該資源。如果調用SwitchToThread函數時沒有其他線程能夠運行,那么該函數返回FALSE,否則返回一個非0值。調用SwitchToThread與調用Sleep是相似的。差別是SwitchToThread允許優先級較低的線程運行;而即使有低優先級線程迫切需要CPU時間,Sleep也能夠立即對調用線程重新進行調度。

13. ? 如何取得線程運行的時間?
(1) ? ? ? 簡單取得線程大概運行時間:
DWORD ? dwStartTime ? = ? 0;
DWORD ? dwEndTime ? = ? 0;
DWORD ? dwRunTime ? = ? 0;
dwStartTime ? = ? GetTickCount( ? ? );
……
……
……
dwEndTime ? = ? GetTickCount( ? ? );
dwRunTime ? = ? dwEndTime ? – ? dwStartTime;
(2) ? ? ? ? 調用GetThreadTimes的函數:
參數含義:
hThread ? 線程句柄
lpCreationTime ? 創建時間:英國格林威治時間
lpExitTime ? 退出時間:英國格林威治時間,如果線程仍然在運行,退出時間則未定義
lpKernelTime ? 內核時間:指明線程執行操作系統代碼已經經過了多少個100ns的CPU時間
lpUserTime ? 用戶時間:指明線程執行應用程序代碼已經經過了多少個100ns的CPU時間
GetProcessTimes是個類似GetThreadTimes的函數,適用于進程中的所有線程(甚至是已經終止運行的線程)。返回的內核時間是所有進程的線程在內核代碼中經過的全部時間的總和。GetThreadTimes和GetProcessTimes這兩個函數在Windows98中不起作用。在Windows98中,沒有一個可靠的機制可供應用程序來確定線程或進程已經使用了多少CPU時間。

14. ? 進程的優先級類有哪些?
優先級類 ? 標識符 ? 描述
實時 ? ? ? ?   REALTIME_PRIORITY_CLASS ? 立即對事件作出響應,執行關鍵時間的任務。會搶先于操作系統組件之前運行。
高 ? ? ? ?    HIGH_PRIORITY_CLASS ? 立即對事件作出響應,執行關鍵時間的任務。
高于正常 ? ? ? ? ABOVE_NORMAL_PRIORITY_CLASS ? 在正常優先級與高優先級之間運行(Windows2000)。
正常 ? ? ? ?   NORMAL_PRIORITY_CLASS ? 沒有特殊調度需求
低于正常 ? ? ? ? BELOW_NORMAL_PRIORITY_CLASS ? 在正常優先級與空閑優先級之間運行(Windows2000)。
空閑 ? ? ? ?   IDLE_PRIORITY_CLASS ? 在系統空閑時運行。
設置方法:
BOOL ? SetPriorityClass( ? HANDLE ? hProcess, ? DWORD ? dwPriority ? );
DWORD ? GetPriorityClass( ? HANDLE ? hProcess ? );
使用命令外殼啟動一個程序時,該程序的起始優先級是正常優先級。如果使用Start命令來啟動該程序,可以使用一個開關來設定應用程序的起始優先級。例如:
c:\> START ? /LOW ? CALC.EXE
Start命令還能識別/BELOWNORMAL、/NORMAL、/ABOVENORMAL、/HIGH和/REALTIME等開關。

15. ? 線程的相對優先級有哪些?
相對優先級 ? 標識符 ? 描述
關鍵時間 ? ? ? ? THREAD_PRIORITY_TIME_CRITICAL ? 對于實時優先級類線程在優先級31上運行,對于其他優先級類,線程在優先級15上運行。
最高 ? ? ? ?   THREAD_PRIORITY_HIGHEST ? 線程在高于正常優先級上兩級上運行。
高于正常 ? ? ? ? THREAD_PRIORITY_ABOVE_NORMAL ? 線程在正常優先級上一級上運行。
正常 ? ? ? ?   THREAD_PRIORITY_NORMAL ? 線程在進程的優先級類上正常運行。
低于正常 ? ? ? ? THREAD_PRIORITY_BELOW_NORMAL ? 線程在低于正常優先級下一級上運行。
最低 ? ? ? ?   THREAD_PRIORITY_LOWEST ? 線程在低于正常優先級下兩級上運行。
空閑 ? ? ? ?   THREAD_PRIORITY_IDLE ? 對于實時優先級類線程在優先級16上運行對于其他優先級類線程在優先級1上運行。
設置方法:
BOOL ? SetThreadPriority( ? HANDLE ? hThread, ? DWORD ? dwPriority ? );
DWORD ? GetThreadPriorityClass( ? HANDLE ? hThread ? );

16. ? 如何避免系統動態提高線程的優先級等級?
  系統常常要提高線程的優先級等級,以便對窗口消息或讀取磁盤等I/O事件作出響應。或者當系統發現一個線程在大約3至4s內一直渴望得到CPU時間,它就將這個渴望得到CPU時間的線程的優先級動態提高到15,并讓該線程運行兩倍于它的時間量。當到了兩倍時間量的時候,該線程的優先級立即返回到它的基本優先級。下面的函數可以對系統的調度方式進行設置:
BOOL ? SetProcessPriorityBoost( ? HANDLE ? hProcess, ? BOOL ? bDisableBoost ? );
BOOL ? GetProcessPriorityBoost( ? HANDLE ? hProcess, ? PBOOL ? pbDisableBoost ? );
BOOL ? SetThreadPriorityBoost( ? HANDLE ? hThread, ? BOOL ? bDisableBoost ? );
BOOL ? GetThreadPriorityBoost( ? HANDLE ? hThread, ? PBOOL ? pbDisableBoost ? );
SetProcessPriorityBoost負責告訴系統激活或停用進行中的所有線程的優先級提高功能,而SetThreadPriorityBoost則激活或停用各個線程的優先級提高功能。Windows98沒有提供這4個函數的有用的實現代碼。



本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/449978.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/449978.shtml
英文地址,請注明出處:http://en.pswp.cn/news/449978.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

AirPods的自動連接配對原理

首次連接 打開裝有 AirPods 的充電盒,并將它放在 iPhone 旁邊。此時你的 iPhone 上將出現設置動畫。輕點「連接」,然后輕點「完成」。 就這么簡單,而且會自動設置,實現與已使用同一 Apple ID 登錄 iCloud 的任一支持設備搭配使用…

Linux chmod命令

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 Linux/Unix 的文件調用權限分為三級 : 文件擁有者、群組、其他。利用 chmod 可以藉以控制文件如何被他人所調用。 使用權限 : 所有使用…

模塊化

我那進了"模塊化研究"小組.所以嘞.研究模塊化以及如何讓項目的模塊化更加合理和高效是我們小組的主要目的.首先,在實行模塊化之前,得先鞏固模塊化開發的理論基礎,因為理論是實踐的基礎。只有這樣,在過程中理論與實踐相結合,才有可能達到最滿意…

1566:基礎練習 十六進制轉八進制

題目地址&#xff1a;https://acmore.cc/problem/LOCAL/1566 1 #include <iostream>2 #include <string>3 4 using namespace std;5 6 string HexToBin(string s) //16進制轉2進制7 {8 string str "";9 for (int i 0; i < s.size(); i) 10…

利用fastjson對json轉map的操作

String str "{\"0\":\"zhangsan\",\"1\":\"lisi\",\"2\":\"wangwu\",\"3\":\"maliu\"}"; //第一種方式 Map maps (Map)JSON.parse(str); System.out.println("這個是用J…

推薦書籍

五百本編程書籍推薦【信息化類】 書號書名作者出版時間定價對應頁碼TP02041企業資源計劃&#xff08;ERP&#xff09;教程羅鴻2006-1&#xffe5;28.00—TP02031ERP理論、方法與實踐周玉清 等2005-12&#xffe5;39.00—TP01059ERP原理設計實施&#xff08;第3版&#xff09;羅…

Linux diffstat命令

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 Linux diffstat命令根據diff的比較結果&#xff0c;顯示統計數字。 diffstat讀取diff的輸出結果&#xff0c;然后統計各文件的插入&…

java命令--jmap命令使用(查找內存泄漏對象)

轉自&#xff1a;https://www.cnblogs.com/kongzhongqijing/articles/3621163.html jdk安裝后會自帶一些小工具&#xff0c;jmap命令(Java Memory Map)是其中之一。主要用于打印指定Java進程(或核心文件、遠程調試服務器)的共享對象內存映射或堆內存細節。 jmap命令可以獲得運行…

tr069相關協議說明

截圖自easycwmp官網&#xff1a;http://easycwmp.org/轉載于:https://www.cnblogs.com/kiss-passion/p/10362029.html

如何revert一個merged branch上所有的改動

開發過程中如果想刪除之前merged的某個branch&#xff0c;并且在merge過該分支之后又進行了多次的提交&#xff0c;可以通過以下命令進行&#xff1a; git revert -n merge_commit_id -m 1 注&#xff1a;該方法適合merge過分支后&#xff0c;沒有基于該branch內容做修改的情況…

Beta 沖刺(6/7)

隊名 火箭少男100組長博客 林燊大哥作業博客 Beta 沖鴨鴨鴨&#xff01;成員沖刺階段情況 林燊&#xff08;組長&#xff09; 過去兩天完成了哪些任務 協調組內工作最終測試文稿編寫展示GitHub當日代碼/文檔簽入記錄(組內共享)接下來的計劃 協助開發組完成標簽制作展示視頻制作…

Linux find命令、Linux rmdir命令、Linux ls命令

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 Linux find命令用來在指定目錄下查找文件。任何位于參數之前的字符串都將被視為欲查找的目錄名。如果使用該命令時&#xff0c;不設置任…

Android Bluetooth BLE相關開發資源匯總

Android開啟藍牙開關 轉載自Android&#xff1a;Bluetooth 的打開和關閉 檢查系統藍牙是否開啟 BluetoothManager bluetoothManager (BluetoothManager) this. getSystemService(Context.BLUETOOTH_SERVICE); BluetoothAdapter mBluetoothAdapter bluetoothManager.getAdapt…

__invoke,try{}catch(){},microtime(),is_callable()

<?php /*1.對象本身不能直接當函數用&#xff0c;如果被當做函數用&#xff0c;會直接回調__invoke方法* 2.驗證變量的內容能否作為函數調用* 3.try{}catch(Exception $e){}catch(){}finally{}* 4.microtime()函數返回當前時間戳和微妙數* */ class httpException extends …

H.264中的I_PCM模式

H.264中的I_PCM模式 I_PCM是一種幀內編碼模式&#xff0c;在該模式下&#xff0c;編碼器直接傳輸圖像的像素值&#xff0c;而不經過預測和變換。在一些特殊的情況下&#xff0c;特別是圖像內容不規則或者量化參數非常低時&#xff0c;該模式比常規的操作&#xff08;幀內預測…

RxPermissions 源碼解析之舉一反三

[toc] RxPermissions 源碼解析 簡介 RxPermissions 是基于 RxJava 開發的用于幫助 在Android 6.0 中處理運行時權限檢測的框架。在 Android 6.0 中增加了對危險權限的動態申請&#xff0c;而不是像 Android 6.0 之前的默認全部獲取的方式。 原始動態權限的獲取 如果按照以往的獲…

總結Selenium WebDriver中一些鼠標和鍵盤事件的使用

在使用 Selenium WebDriver 做自動化測試的時候&#xff0c;會經常模擬鼠標和鍵盤的一些行為。比如使用鼠標單擊、雙擊、右擊、拖拽等動作&#xff1b;或者鍵盤輸入、快捷鍵使用、組合鍵使用等模擬鍵盤的操作。在 WebDeriver 中&#xff0c;有一個專門的類來負責實現這些測試場…

最快浮點數取絕對值

做視頻算法10多年&#xff0c;經常要算絕對值&#xff0c;整數的絕對值有快速算法&#xff0c;但浮點數的絕對值沒看到有快速算法&#xff0c;經常不段發現&#xff0c;得到如下浮點數的快速算法&#xff1a; 快6倍多&#xff0c; #include <Windows.h> #include <ios…

Linux ln命令、軟鏈接和硬鏈接的區別

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 Linux ln命令是一個非常重要命令&#xff0c;它的功能是為某一個文件在另外一個位置建立一個同步的鏈接。 當我們需要在不同的目錄&…

Android應用開發——文件目錄

Android 存儲位置及 API 一、內部存儲 應用安裝后都會在Android 根目錄生成 /data/data/packagename&#xff0c;當前應用讀取不需要讀寫權限 注意&#xff1a; 有些開發者可能看到過應用的根目錄為 /data/user/0/packagename 的情況&#xff0c;這里解釋一下&#xff0c;And…