1. 程式人生 > >PoEdu - Windows階段班 【Po學校】Lesson006_線程_線程的啟動到消亡 &線程狀態 & 線程安全 & CONTEXT結構體 & 令牌鎖

PoEdu - Windows階段班 【Po學校】Lesson006_線程_線程的啟動到消亡 &線程狀態 & 線程安全 & CONTEXT結構體 & 令牌鎖

turn within 周期 銷毀 hose pen inf obj objects

    • 011_線程啟動到死亡的詳細講解

      • 1. 線程內核對象
        • 使用計數 2 ##決定當前線程何時銷毀
        • 暫停計數 1 ##UINT類型初始為1,可以暫停多次,如置為0則取消暫停。
        • 退出代碼 STILL_ACTIVE
        • Signaled FALSE
        • CONTEXT 為空
      • 2. 棧
        ##在隸屬於當前進程的空間中,分配一塊“棧”空間,以供線程使用
        • 參數 lpParam
        • 入口地址 lpfnAddr
      • 3. CONTEXT
        ##線程上一次運行時的寄存器
        • IP(指令寄存器) void RtlUserThreadStart(未公開的函數)(lpParam,lpFnAddr)
        • SP(棧寄存器) lpFnAddr
      • 4. 交給CPU調度
      • 5. RtlUserThreadStart
        • SEH ##設置結構化異常
        • 調用線程函數,傳遞lpParam
        • 等待線程函數的返回
        • ExitThread ## 使用計數遞減
    • 012_beginthreadex和CreateThread

      • 多線程運行庫的設置 技術分享
      • _beginthreadex() 函數隸屬於C標準的運行庫,要包含頭文件: process.h
      • 非線程安全:
        • 多線程訪問全局變量時,會有出錯的機率。
        • 舉例C語言的錯誤處理機制:errno 非線程安全
        • C語言的設計者,為了解決線程的安全問題,給出了_beginthreadex()函數。
      • _beginthreadex()函數的區別:
        • 1 參數與 CreateThread()函數的參數意義相同,但其類型已經不同。
        • 2 beginthreadex()比CreateThread()函數,多開辟了一段空間,分配在堆上面,存儲一些全局的變量。以期線程安全。多分配了堆空間後,才來調用CreateThread();
        • 3 使用beginthreadex()要配套_endthreadex()使用.
      • 建議使用_beginthreadex(),有多分配一段堆空間。
        • _beginthread()函數不建議使用,因為其並沒有多分配一段堆空間。這裏要註意使用EX版本的函數。
    • 013_線程狀態

      技術分享
    • 014_線程的掛起轉態

      • 啟動

        • CONTEXT 初始
        • 使用計數 置為2
        • 暫停計數 置為1
          • 在後續CreateThread完成後,減1得出0,為0則進入CPU的調度。當前線程是可執行的狀態。
      • 運行

        • 執行我們的函數
          • 時不時的切換線程,將CPU寄存器的狀態寫入CONTEXT
          • 切換到當前時,先讀取CONTEXT
      • 掛起

        • SuspendThread() 32位 Wow64SuspendThread() 64位
          • 調用暫停線程函數SuspendThread(),函數會把暫停計數+1
          • SuspendThread函數會返回0,第一次調用結束時返回1;第二次調用時返回1,第二次結束時返回2. 此函數有調用時的返回,與結束時的返回。
        • ResumeThread() 恢復掛起線程
          • 調用此函數,會把當前線程的暫停計數-1
          • 有幾次掛起,就要有幾次恢復調用,不然線程仍然不會進入運行。
        • 不建議使用線程的掛起:
          • 如果線程入口函數裏面,有new新的堆空間,而操作系統切換線程時,有可能會使得這塊堆空間,在被占用的情況下,卻沒有占用的標記。此時當此堆空間被訪問時,就出了非常隱蔽的BUG。
          • 要區分2種掛起:
          • 1 操作系統切換線程時的“掛起”——在“池”中
            • 準確來說是“切換”線程。按操作系統的算法,線程在進行CPU調度時,所產生的暫停。
          • 2 SuspendThread()函數產生的掛起 ——不在“池”中
            • 掛起的線程被拿出CPU的線程運行調度池
    • 015_線程 等待、休眠、及饑餓線程

      • 等待休眠 Sleep()

        • Sleep(100); 表示休眠100毫秒後,CPU再來運行;
        • 這裏100毫秒並不能準確不差,因為windows操作系統非實時的,CPU的運行時調度也是非實時的;所以在時間方面有一點誤差,只能說是無限接近100毫秒。
        • 放棄當前的時間片,在一段時間之內,CPU不會調度此線程
        • Sleep(INFINITE) 永遠等待
          • INFINITE 其值為-1;
          • 一直等待到進程結束
        • Sleep(0) 放棄線程執行時間片
        • SwitchToThread() 把CPU剩余的時間片,分配給"饑餓度"較高的線程.
          • 調度另外一個線程,也就是把CPU的執行周期給另外一個線程身上。
          • CPU時間片“饑餓度”:如果一此線程相對時間片很少,或者一直沒有得到執行,我們就稱其為“饑餓”線程。饑餓度相對較高。
    • 016_CONTEXT結構體

      • 源碼
        typedef struct _CONTEXT {
            //
            // The flags values within this flag control the contents of
            // a CONTEXT record.
            //
            // If the context record is used as an input parameter, then
            // for each portion of the context record controlled by a flag
            // whose value is set, it is assumed that that portion of the
            // context record contains valid context. If the context record
            // is being used to modify a threads context, then only that
            // portion of the threads context will be modified.
            //
            // If the context record is used as an IN OUT parameter to capture
            // the context of a thread, then only those portions of the thread‘s
            // context corresponding to set flags will be returned.
            //
            // The context record is never used as an OUT only parameter.
            //
            DWORD ContextFlags;
            //
            // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is
            // set in ContextFlags.  Note that CONTEXT_DEBUG_REGISTERS is NOT
            // included in CONTEXT_FULL.
            //
            DWORD   Dr0;
            DWORD   Dr1;
            DWORD   Dr2;
            DWORD   Dr3;
            DWORD   Dr6;
            DWORD   Dr7;
            //
            // This section is specified/returned if the
            // ContextFlags word contians the flag CONTEXT_FLOATING_POINT.
            //
            FLOATING_SAVE_AREA FloatSave;
            //
            // This section is specified/returned if the
            // ContextFlags word contians the flag CONTEXT_SEGMENTS.
            //
            DWORD   SegGs;
            DWORD   SegFs;
            DWORD   SegEs;
            DWORD   SegDs;
            //
            // This section is specified/returned if the
            // ContextFlags word contians the flag CONTEXT_INTEGER.
            //
            DWORD   Edi;
            DWORD   Esi;
            DWORD   Ebx;
            DWORD   Edx;
            DWORD   Ecx;
            DWORD   Eax;
            //
            // This section is specified/returned if the
            // ContextFlags word contians the flag CONTEXT_CONTROL.
            //
            DWORD   Ebp;
            DWORD   Eip;
            DWORD   SegCs;              // MUST BE SANITIZED
            DWORD   EFlags;             // MUST BE SANITIZED
            DWORD   Esp;
            DWORD   SegSs;
            //
            // This section is specified/returned if the ContextFlags word
            // contains the flag CONTEXT_EXTENDED_REGISTERS.
            // The format and contexts are processor specific
            //
            BYTE    ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
        } CONTEXT;
        

          


    • 017_線程安全及上鎖

      • 示例
      • // ContextDemo.cpp : 定義控制臺應用程序的入口點。
        //
        
        #include "stdafx.h"
        #include <windows.h>
        #include <process.h>
        
        BOOL bUseing = FALSE;
        
        unsigned int __stdcall ThreadRun(void* lParam)
        {
        	int nNum = 0;
        	while (true)
        	{
        		if (!bUseing)
        		{
        			bUseing = TRUE;
        			_tprintf(TEXT("ThreadRun:%d\r\n"), nNum++);
        			bUseing = FALSE;
        		}
        	}
        
        }
        
        unsigned int __stdcall ThreadMonitor(void* lParam)
        {
        	HANDLE hThread = (HANDLE)(lParam);
        	while (true)
        	{
        		CONTEXT context;
        		context.ContextFlags = CONTEXT_ALL;
        
        		SuspendThread(hThread);
        		GetThreadContext(hThread, &context);
        		if (!bUseing)
        		{
        			bUseing = TRUE;				
        			_tprintf(TEXT("EAX:0x%x ESP:0x%x EIP:0x%x\r\n"), context.Eax, context.Esp, context.Eip);			
        			bUseing = FALSE;
        		}	
        		ResumeThread(hThread);
        	}
        }
        
        int main()
        {
        	HANDLE hThreads[2];
        	hThreads[0] = (HANDLE)_beginthreadex(nullptr, 0, ThreadRun,nullptr, 0, nullptr);
        	hThreads[1] = (HANDLE)_beginthreadex(nullptr, 0, ThreadMonitor,hThreads[0], 0, nullptr);
        	WaitForMultipleObjects(sizeof(hThreads)/sizeof(HANDLE),hThreads,true,INFINITE);
        	for (int i = 0; i<sizeof(hThreads)/sizeof(HANDLE);++i)
        	{
        		CloseHandle(hThreads[i]);
        	}
            return 0;
        }
        

         

PoEdu - Windows階段班 【Po學校】Lesson006_線程_線程的啟動到消亡 &線程狀態 & 線程安全 & CONTEXT結構體 & 令牌鎖