日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

3.线程等待与唤醒

發布時間:2025/3/20 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 3.线程等待与唤醒 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

我們在之前的講解了如何自己實現臨界區以及什么是Windows自旋鎖,這兩種同步方案在線程無法進入臨界區時都會讓當前線程進入等待狀態,一種是通過Sleep函數實現的,一種是通過讓當前的CPU"空轉”實現的,但這兩種等待方式都有局限性:

  • 通過Sleep函數進行等待,等待時間該如何確定呢?
  • 通過“空轉”的方式進行等待,只有等待時間很短的情況下才有意義,否則對CPU資源是種浪費。而且自旋鎖只能在多核的環境下才有意義。
  • 有沒有更加合理的等待方式呢?只有在條件成熟的時候才將當前線程喚醒?

    等待與喚醒機制

    在Windows中,一個線程可以通過等待一個或者多個可等待對象,從而進入等待狀態,另一個線程可以在某些時刻喚醒等待這些對象的其他線程。

    A線程用WaitForSingleObject 或者 WaitForMultipleObjects進入等待狀態,B線程用SetEvent將線程喚醒,A線程與B線程就是通過 可等待對象 聯系到一起的。

    可等待對象

    在Windbg中查看如下結構體:

    進程 dt KPROCESS 線程 dt KTHREAD 定時器 dt_ KTIMER 信號量 dt KSEMAPHOREI 事件 dt KEVENT 互斥體 dt KMUTANT 文件 dt FILE OBJECT

    可等待對象都有一個共同的特點,他們的第一個成員都是一個結構體:_DISPATCHER_HEADER。但是有一些結構體并不是比如說文件就不是。

    只要結構體中有_DISPATCHER_HEADER就是可等待對象。

    kd> dt _DISPATCHER_HEADER nt!_DISPATCHER_HEADER+0x000 Type //該對象類型+0x001 Absolute +0x002 Size +0x003 Inserted+0x004 SignalState //該分發器信號狀態 (值大于0就是有信號 分發器對象也稱為同步對象)+0x008 WaitListHead //雙向鏈表頭,鏈著所有等待塊(此鏈表包含了所有正在等待該分發器對象的線程)

    可等待對象的差異

    WaitForSingleObject(3)NtWaitorSingleObject(內核) 1)通過3環用戶提供的句柄,找到等待對象的內核地址。 2)如果是以_DISPATCHER-HEADER開頭,直接使用。 3)如果不是以DISPATCHER HEADER開頭的對象,則找到在其中嵌入的_DISPATCHER_HEADER對象。KeWaitForSingleObject(內核) -------核心功能,后面在將

    測試代碼

    #include <stdio.h> #include<windows.h>HANDLE hEvent[2];VOID WINAPI ThreadProc(LPVOID text) {::WaitForSingleObject(hEvent[0], -1);printf("ThreadProc函數執行...\n"); }int main() {hEvent[0] = ::CreateEvent(NULL, TRUE, FALSE, NULL);::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, NULL, 0, NULL);getchar();return 0; }

    windbg中可以通過kd> !process 892ec020 來查看該進程中所有的線程。

    還可以通過查看等待鏈表來找到該線程,使用了WaitForSingleObject此線程就會出現在等待鏈表中。

    (kd> dt _KTHREAD 895458b8)

    WaitBlockList 等待塊,當前線程就是等待塊與被等待塊聯系到一起的。

    等待塊中的每個成員的值

    kd> dt _KWAIT_BLOCK 0x89545928 nt!_KWAIT_BLOCK+0x000 WaitListEntry : _LIST_ENTRY [ 0x89320290 - 0x89320290 ]+0x008 Thread : 0x895458b8 _KTHREAD //當前線程+0x00c Object : 0x89320288 Void //被等待對象的地址+0x010 NextWaitBlock : 0x89545928 _KWAIT_BLOCK /*單向循環鏈表,如果當前線程只有一個等待對象它指向自己,多個等待對象就是第一個指向第二個,最后一個指向第一個*/+0x014 WaitKey : 0 //當等待塊索引,我這是第0個+0x016 WaitType : 1 /*等待時要求只要有一個等待對象符合條件就可以被激活那么它的值就是1, 如果如果你等待多個對象必須全部符合條件才可以被激活它就是0*/



    下面來看看一個線程等待多個對象的情況。

    #include <stdio.h> #include<windows.h>HANDLE hEvent[2];VOID WINAPI ThreadProc(LPVOID text) {::WaitForMultipleObjects(2, hEvent, FALSE, -1);printf("ThreadProc函數執行...\n"); }int main() {DWORD thread_id_1, thread_id_2;hEvent[0] = ::CreateEvent(NULL, TRUE, FALSE, NULL);hEvent[1] = ::CreateEvent(NULL, TRUE, FALSE, NULL);::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, NULL, 0, (LPDWORD)&thread_id_1);getchar();return 0; }

    windbg中查看情況

    +0xc Object指向被等待對象。

    被等待對象的 WaitListHead 是一個雙向鏈表,這個鏈表鏈鏈著所有等待塊。

    比如說你當前等待的這個對象可能也被別的線程等待著,WaitListHead就把等待它的所有線程的_KWAIT_BLOCK 穿到他的第一個成員中( +0x000 WaitListEntry)。

    WaitListHead是鏈表頭它鏈著所有等待塊,掛的位置就是_KWAIT_BLOCK .WaitListEntry。



    等待網

    如圖:線程1正在等待分發器對象B,線程2正在等待分發器對象A和B。

    當對象B變成有信號狀態時,線程1的等待條件已滿足,于是他變成延遲的就緒狀態,以便被調度和執行;
    而線程2的等待條件是否滿足,要決取于它的等待類型,KWAIT_BLOCK中的WaitType記錄了線程的等待類型,若是WaitAny則線程2的等待條件滿足;若是WaitAll線程2繼續等待。

    當對象A變成有信號狀態時,線程2的等待條件滿足,于是他變成延遲的就緒狀態,以便被調度和執行。

    總結:

  • 等待中的線程,一定在等待鏈表中(KiWaitListHead),同時也一定在這張網上 (KTHREAD +5C的位置不為空)。
  • 線程通過調用WaitForSingleObject/WaitForMultipleObjects函數將自己掛到這 ,張網上。
  • 線程什么時候會再次執行取決于其他線程何時調用相關函數,等待對象不同調用的函數也不同
  • 總結

    以上是生活随笔為你收集整理的3.线程等待与唤醒的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。