Windows API(3)スレッド

24493 ワード

Windows APIノート(一)カーネルオブジェクトWindows APIノート(二)プロセスとプロセス間通信、プロセス境界Windows APIノート(三)スレッドとスレッド同期、スレッドローカルストレージWindows APIノート(三)スレッド同期Windows APIノート(四)win 32メモリ構造Windows APIノート(五)仮想メモリWindows APIノート(六)メモリマッピングファイルWindows APIノート(七)スタックWindows APIノート(八)ファイルシステムWindows APIノート(九)ウィンドウメッセージWindows APIノート(十)ダイナミックリンクライブラリWindows APIノート(十一)デバイスI/O
文書ディレクトリ
  • 1. スレッド
  • の作成
  • 2. スレッド関数
  • 2.1スレッドのスタック
  • 2.2スレッドのContext構造
  • 2.3スレッドの実行時間
  • 3. 終了スレッド
  • 3.2プロセス終了プロセス
  • 4. 自分のアイデンティティを識別する
  • 5. システムがスレッド
  • をスケジュールする方法
  • 5.1 Windows API付与優先度
  • 5.1.1プロセス優先度
  • 5.1.2スレッド優先度
  • 5.1.3スレッド相対優先度
  • 5.3スレッドとプロセスの保留とリカバリ
  • 6. システム内部状況
  • 7. プロセス、スレッド、およびCランタイムライブラリ
  • いつ作成しますか?作成する必要がないのはいつですか?
  • より多くのスレッドを作成することは、より多くのCPU時間
  • を占有することを主導するプロセスの完了に役立つ.
  • スレッドスケジューリング時間オーバヘッド
  • スレッドは、いくつかの問題を解決するときに新しい問題(例えば、スレッド同期の問題)
  • を発生する.
    1.スレッドの作成
    HANDLE CreateThread(
        _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,	//      ,           
        _In_ SIZE_T dwStackSize,							//        ,0       (1MB)
        _In_ LPTHREAD_START_ROUTINE lpStartAddress,			//       
        _In_opt_ __drv_aliasesMem LPVOID lpParameter,		//       
        _In_ DWORD dwCreationFlags,							//        ;0       ;CREATE_SUSPENDED      ,      
        _Out_opt_ LPDWORD lpThreadId						//     id
        );
    
  • スレッドカーネルオブジェクト(スレッドハンドル)を割り当てて、新しく作成されたスレッド
  • を識別および管理する.
  • このスレッドの終了コードをSTILL_に初期化するACTIVE、スレッド保留カウント(使用カウント)は1
  • に設定.
  • 新しいスレッドにCONTEXT構造(CPU時間)
  • を割り当てる.
  • スレッドにアドレス空間を割り当てる(2ページ)
  • CreateThreadのlpStartAddr値とlpvThread値をスタックの上部に配置し、StartOfThread
  • に渡す
  • スレッドのCONTEXT構造におけるスタックポインタレジスタを第5ステップでスタックトップに置く値を指す.命令ポインタレジスタを内部のStartOfThread関数
  • に向ける.
    2.スレッド関数
    #include 
    #include 
    
    DWORD WINAPI MyThreadFunc(LPVOID lpParm)
    {
        Sleep(3 * 1000);
    
        LPDWORD mainThreadId = (LPDWORD)lpParm;
        DWORD tid = GetCurrentThreadId();
        printf("Main Thread Id : %ld , Current Thread Id : %ld
    "
    , *mainThreadId, tid); return 0; } void CreateThreadFunc() { DWORD mainThreadId = GetCurrentThreadId(); SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(sa); sa.bInheritHandle = false; // sa.lpSecurityDescriptor = NULL; // DWORD tid; HANDLE thnd = CreateThread(&sa, 0, MyThreadFunc, &mainThreadId, 0, &tid); printf("create thread id : %d
    "
    , tid); CloseHandle(thnd); // , , mainThreadId , mainThreadId // Sleep(10*1000); } int main() { CreateThreadFunc(); Sleep(10 * 1000); system("pause"); }

    2.1スレッドのスタック
  • 各スレッドは、存在するプロセスの4 GBアドレス空間から独自のスタック
  • を割り当てる.
  • 静的変数またはグローバル変数を使用する場合、複数のスレッドがこれらの変数
  • に同時にアクセスする.
  • ローカル変数と自動変数は、スレッドのスタックに作成された
  • です.
    2.2スレッドのContext構造
    ContextはCPUレジスタの状態を格納している.
    //          SuspendThread    ,       
    BOOL GetThreadContext(
        _In_ HANDLE hThread,
        _Inout_ LPCONTEXT lpContext
        );
    
    //
    // Context Frame
    //
    //  This frame has a several purposes: 1) it is used as an argument to
    //  NtContinue, 2) it is used to constuct a call frame for APC delivery,
    //  and 3) it is used in the user level thread creation routines.
    //
    //
    // The flags field within this record controls 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 is modified.
    //
    // If the context record is used as an output parameter to capture the
    // context of a thread, then only those portions of the thread's context
    // corresponding to set flags will be returned.
    //
    // CONTEXT_CONTROL specifies SegSs, Rsp, SegCs, Rip, and EFlags.
    //
    // CONTEXT_INTEGER specifies Rax, Rcx, Rdx, Rbx, Rbp, Rsi, Rdi, and R8-R15.
    //
    // CONTEXT_SEGMENTS specifies SegDs, SegEs, SegFs, and SegGs.
    //
    // CONTEXT_FLOATING_POINT specifies Xmm0-Xmm15.
    //
    // CONTEXT_DEBUG_REGISTERS specifies Dr0-Dr3 and Dr6-Dr7.
    //
    
    typedef struct DECLSPEC_ALIGN(16) _CONTEXT {
    
        //
        // Register parameter home addresses.
        //
        // N.B. These fields are for convience - they could be used to extend the
        //      context record in the future.
        //
    
        DWORD64 P1Home;
        DWORD64 P2Home;
        DWORD64 P3Home;
        DWORD64 P4Home;
        DWORD64 P5Home;
        DWORD64 P6Home;
    
        //
        // Control flags.
        //
    
        DWORD ContextFlags;
        DWORD MxCsr;
    
        //
        // Segment Registers and processor flags.
        //
    
        WORD   SegCs;
        WORD   SegDs;
        WORD   SegEs;
        WORD   SegFs;
        WORD   SegGs;
        WORD   SegSs;
        DWORD EFlags;
    
        //
        // Debug registers
        //
    
        DWORD64 Dr0;
        DWORD64 Dr1;
        DWORD64 Dr2;
        DWORD64 Dr3;
        DWORD64 Dr6;
        DWORD64 Dr7;
    
        //
        // Integer registers.
        //
    
        DWORD64 Rax;
        DWORD64 Rcx;
        DWORD64 Rdx;
        DWORD64 Rbx;
        DWORD64 Rsp;
        DWORD64 Rbp;
        DWORD64 Rsi;
        DWORD64 Rdi;
        DWORD64 R8;
        DWORD64 R9;
        DWORD64 R10;
        DWORD64 R11;
        DWORD64 R12;
        DWORD64 R13;
        DWORD64 R14;
        DWORD64 R15;
    
        //
        // Program counter.
        //
    
        DWORD64 Rip;
    
        //
        // Floating point state.
        //
    
        union {
            XMM_SAVE_AREA32 FltSave;
            struct {
                M128A Header[2];
                M128A Legacy[8];
                M128A Xmm0;
                M128A Xmm1;
                M128A Xmm2;
                M128A Xmm3;
                M128A Xmm4;
                M128A Xmm5;
                M128A Xmm6;
                M128A Xmm7;
                M128A Xmm8;
                M128A Xmm9;
                M128A Xmm10;
                M128A Xmm11;
                M128A Xmm12;
                M128A Xmm13;
                M128A Xmm14;
                M128A Xmm15;
            } DUMMYSTRUCTNAME;
        } DUMMYUNIONNAME;
    
        //
        // Vector registers.
        //
    
        M128A VectorRegister[26];
        DWORD64 VectorControl;
    
        //
        // Special debug control registers.
        //
    
        DWORD64 DebugControl;
        DWORD64 LastBranchToRip;
        DWORD64 LastBranchFromRip;
        DWORD64 LastExceptionToRip;
        DWORD64 LastExceptionFromRip;
    } CONTEXT, *PCONTEXT;
    

    2.3スレッドの実行時間
    BOOL GetThreadTimes(
        _In_ HANDLE hThread,				//     
        _Out_ LPFILETIME lpCreationTime,	//        
        _Out_ LPFILETIME lpExitTime,		//        ,        
        _Out_ LPFILETIME lpKernelTime,		//                
        _Out_ LPFILETIME lpUserTime			//                
        );
    

    この関数を使用して、複雑なアルゴリズムに必要な時間を判断できます.
    プロセスの実行時間もあります.
    BOOL GetProcessTimes(
        _In_ HANDLE hProcess,							//     
        _Out_ LPFILETIME lpCreationTime,			//       
        _Out_ LPFILETIME lpExitTime,					//        
        _Out_ LPFILETIME lpKernelTime,				//            
        _Out_ LPFILETIME lpUserTime					//            
        );
    

    3.スレッドの終了
    終了するには、次の3つの方法があります.
  • ExitThread
  • を呼び出す
  • TerminateThread(他のプロセスのスレッドを殺すこともできる)
  • を呼び出す
  • このスレッドを含むプロセスが終了した(プロセスが終了し、プロセス内のすべてのスレッドが終了する)
  • .
    3.2プロセス終了のプロセス
  • スレッドが所有するすべてのユーザオブジェクトハンドルが解放される.Win 32では、スレッドが作成するオブジェクトの大部分は所属するプロセスの所有であるが、スレッドはウィンドウとフック(Hook)の2つのユーザオブジェクトを持つことができる.他のオブジェクトは、プロセスの終了時にのみ
  • を解放する.
  • スレッドカーネルオブジェクトの状態が信号状態になる(WaitForSingleObject/WaitForMultipleObjectsは信号がある)
  • スレッドの終了コードは、STILL、ACTIVEからExitThreadまたはTerminateThreadが伝達する値
  • に変化する.
  • スレッドがプロセスの最後のアクティブなスレッドである場合、プロセスも
  • を終了する.
  • スレッドカーネルオブジェクトの使用数-1
  • 4.自分のアイデンティティを識別する
    /*
              ,      ,        
         CloseHandle          
                          
    */
    HANDLE GetCurrentProcess(VOID)
    /*          */
    HANDLE GetCurrentThread(VOID)
    
    /*             */
    HANDLE hProcess;
    DuplicateHandle(
    	GetCurrentProcess(),
    	GetCurrentProcess(),
    	GetCurrentProcess(),
    	&hProcess,
    	0,
    	FALSE,
    	DUPLICATE_SAME_ACCESS);
    

    5.システムがスレッドをどのようにスケジューリングするか
  • スレッド優先度(最小0-最大31)
  • 優先度0は、システム内のゼロページ(Zero Page)という特別なスレッドに与えられ、他のスレッドが0優先度
  • を持つことはできない.
  • CPUは、同じ優先度のすべてのスレッド(常に優先度が31のスレッドがある場合、他の優先度が31より低いスレッドはCPUを割り当てる機会がなく、永遠に実行されない.この場合を飢餓starvationと呼び、一部のスレッドがすべてのCPU時間を占有し、他のスレッドが永遠に実行できない場合に飢餓が発生する)
  • .
  • スレッドが保留するとCPU
  • は得られない.
  • システムは、より高いレベルのスレッドが実行される準備ができていることを発見し、すぐに低レベルのスレッド(タイムスライスの中間にある場合でも)を掛け、完全なCPUを高レベルのスレッドに割り当て、低レベルのスレッドが何をしているかにかかわらず、高レベルのスレッドは常に低レベルのスレッド
  • を先取りする.
    5.1 Windows API優先度付与
    5.1.1プロセス優先度
    クラス#クラス#
    CreateProcessフラグ
    レベル
    空き
    IDLE_PRIORITY_CLASS
    4
    通常(Normal)
    NORMAL_PRIORITY_CLASS
    8
    ハイ(High)
    HIGH_PRIORITY_CLASS
    13
    リアルタイム(Realtime)
    REALTIME_PRIORITY_CLASS
    24
    プロセスCreateProcessの作成時に優先度フラグをfdwCreateフラグに渡します.標準の優先度をデフォルトに設定します.
    プログラムが他のプログラムの実行に干渉したくない場合は、プログラム優先度をアイドルに設定し、他のプログラムがアイドルの場合にのみ実行できます.スクリーンセーバーは、ユーザが特定の期間空いている場合、スクリーンセーバーが自分をアクティブにする良い例です.
    /*       */
    BOOL SetPriorityClass(
        _In_ HANDLE hProcess,		//     
        _In_ DWORD dwPriorityClass	//    
        );
    

    プロセスがフロントに移ると、タイムスライスのサイズが変わります.
    5.1.2スレッド優先度
    スレッドが作成されると、優先度はプロセスの優先度です.プロセス内でSetThreadPriority関数を呼び出すことで、スレッドの相対的な優先度を変更できます.
    //          
    BOOL SetThreadPriority(
        _In_ HANDLE hThread,		//    
        _In_ int nPriority			//     
        );
        
    //           
    int GetThreadPriroty(HANDLE hThread);
    

    5.1.3スレッド相対優先度
    識別子
    意味
    THREAD_PRIORITY_LOWEST
    プロセス優先度-2
    THREAD_PRIORITY_BELOW_NORMAL
    プロセス優先度-1
    THREAD_PRIORITY_NORMAL
    プロセス優先度(デフォルト)
    THREAD_PRIORITY_ABOVE_NORMAL
    プロセス優先度+1
    THREAD_PRIORITY_HIGHEST
    所属プロセス優先度+2
    THREAD_PRIORITY_IDLE
    スレッド優先度を1、プロセス優先度をリアルタイムにすると16(プロセス優先度-15)
    THREAD_PRIORITY_TIME_CRITICAL
    スレッド優先度を15、プロセス優先度をリアルタイムとすると31
    スレッド相対優先度
    IDLE_PRIORITY_CLASS(アイドル)
    NORMAL_PRIORITY_CLASS(通常)
    HIGH_PRIORITY_CLASS(高)
    REALTIME_PRIORITY_CLASS(リアルタイム)
    THREAD_PRIORITY_IDLE(アイドル)
    1
    1
    1
    16
    THREAD_PRIORITY_LOWEST(最低)
    2
    6
    11
    22
    THREAD_PRIORITY_BELOW_NORMAL(通常より低い)
    3
    7
    12
    23
    THREAD_PRIORITY_NORMAL(普通)
    4
    8
    13
    24
    THREAD_PRIORITY_ABOVE_NORMAL(通常より高い)
    5
    9
    14
    25
    THREAD_PRIORITY_HIGHEST(最高)
    6
    10
    15
    26
    THREAD_PRIORITY_TIME_CRITICAL(タイムキー)
    15
    15
    15
    31
    5.3スレッドとプロセスの保留とリカバリ
    保留カウント
    6.システム内部の状況
    プロセス内のスレッドの表示
    7.プロセス、スレッドおよびCランタイムライブラリ
    MicrosoftはVC++に6のCランタイムライブラリを付属しています.
    ライブラリ名
    説明
    LIBC.LIB
    静的リンクライブラリのリリースバージョン、単一スレッドの適用
    LIBCD.LIB
    静的リンクライブラリのデバッグバージョン、単一スレッドの適用
    LIBCMT.LIB
    静的リンクライブラリのリリースバージョン、マルチスレッドの応用
    LIBCMTD.LIB
    静的リンクライブラリのデバッグバージョン、マルチスレッドの応用
    MSVCRT.LIB
    ダイナミックリンクライブラリMSVCRT.DLLのリリースバージョンの導入ライブラリは、単一スレッドとマルチスレッドの応用に使用されます.
    MSVCRTD.LIB
    ダイナミックリンクライブラリMSVCRTD.DLLのデバッグバージョンの導入ライブラリは、マルチスレッドと単一スレッドの応用に使用されます.
    スレッドを作成するときは、_を使用しないでください.beginthreadと_endthread、使用すべき_beginthreadexと_endthreadex. _beginthreadと_endthreadは設計が古く、スレッド優先度の設定はありません.