精华内容
下载资源
问答
  • 线程ID线程句柄有啥不同

    千次阅读 2020-06-24 09:44:12
    线程句柄线程ID的区别: ●CreateThread() API 用于创建线程。 API 返回同时线程句柄和线程标识符 (ID)。 线程句柄有完全访问权创建线程对象。 运行线程时线程 ID 唯一标识线程在系统级别。 ●ID是在Windows系统...

    当某个程序创建一个线程后,会产生一个线程的句柄,线程的句柄主要用来控制整个线程的运行,例如停止、挂起或设置线程的优先级等操作。

    线程句柄与线程ID的区别:

    ●CreateThread() API 用于创建线程。 API 返回同时线程句柄和线程标识符 (ID)。 线程句柄有完全访问权创建线程对象。 运行线程时线程 ID 唯一标识线程在系统级别。
    ●ID是在Windows系统范围内唯一标示Thread的。
    ●Handle是用来操作Thread的,可以有多个,每个HANDLE可以有不同的操作权限,在不同进程OpenThread得到的值不一样。
    ●线程的ID是系统全局的,其HANDLE是进程局部的.

    ●此ID只在线程的生存期内有效。

    ●HANDLE是os和client之间用来操作进程和线程一个桥梁,os有一张维护HANDLE的表单,里面大概放置了 HANDLE的引用计数和有关的属性,HANDLE是os标识进程和线程的东西,但是用户也可以用这个来标识进程和线程,对其操作;而ID是os用来标识进程和线程的,并且是全局唯一的, 但用户可以通过这个ID获得进程线程的HANDLE,多次得到的HANDLE并不一定是一样的.HANDLE是内核对象,而ID好像不是,并没有专门创建ID的函数.

    ●ID是CreateThread时操作系统自动生成的。

    ●线程的句柄和id是不同的。
    在windows系统中,线程的id是唯一对应的,也就是说,如果两个线程返回相同的id,则他们必然是同一线程,反之一定是不同的线程。而线程的句柄并不是线程的唯一标识,线程的句柄只是用来访问该线程的的一个32位值,尽管相同的句柄一定标识同一线程,但同一线程可能拥有两个打开的句柄,因此,不能用句柄来区分两个线程是否是同一线程。

    什么是句柄:

    句柄是一种指向指针的指针。我们知道,所谓指针是一种内存地址。应用程序启动后,组成这个程序的各对象是住留在内存的。如果简单地理解,似乎我们只要获知这个内存的首地址,那么就可以随时用这个地址访问对象。但是,如果您真的这样认为,那么您就大错特错了。我们知道,Windows是一个以虚拟内存为基础的操作系统。在这种系统环境下,Windows内存管理器经常在内存中来回移动对象,依此来满足各种应用程序的内存需要。对象被移动意味着它的地址变化了。如果地址总是如此变化,我们该到哪里去找该对象呢?
    为了解决这个问题,Windows操作系统为各应用程序腾出一些内存储地址,用来专门登记各应用对象在内存中的地址变化,而这个地址(存储单元的位置)本身是不变的。Windows内存管理器在移动对象在内存中的位置后,把对象新的地址告知这个句柄地址来保存。这样我们只需记住这个句柄地址就可以间接地知道对象具体在内存中的哪个位置。这个地址是在对象装载(Load)时由系统分配给的,当系统卸载时(Unload)又释放给系统。
    句柄地址(稳定)→记载着对象在内存中的地址→对象在内存中的地址(不稳定)→实际对象

    线程与线程句柄的关系:

    句柄可以认为是系统对资源(如线程)的分配的一个编号。关闭这个编号,对于不同的资源,效果不尽相同。对于线程来说,关闭这个编号并不意味着终止线程,只是之后很难再操纵这个线程。

    原文:线程ID与线程句柄区别

    展开全文
  • CreateThread, 会返回线程句柄, 同时, 传递的参数会给线程ID赋值. 昨日读了 windows 多线程程序设计. 知道了线程句柄线程ID的区别. 线程ID: 我目前已知的有一个用途: 向一个线程投递消息. 线程句柄: 基本上...
    CreateThread, 会返回线程句柄, 同时, 传递的参数会给线程ID赋值.
    

    昨日读了 windows 多线程程序设计. 知道了线程句柄和线程ID的区别.

    线程ID: 我目前已知的有一个用途: 向一个线程投递消息.
    线程句柄: 基本上, 所有操作线程的windowsAPI均使用线程句柄.

    每多一个句柄, 线程对象就多一个引用计数, 当引用计数为0时, 线程对象将被系统回收.
    一个线程对象可以有很多个句柄指向它.

    我们经常看到这样的代码:
     HANDLE hThrd = CreateThread( NULL, 0, HeartBeatThread, NULL, 0, NULL );
     if( hThrd )
     {
      CloseHandle( hThrd );
      hThrd = 0;
     }
    创建一个线程后, 就把该句柄close了. 把该句柄close,会不会导致线程被回收呢? 
    答案是:不会
    因为: CreateThread函数创建一个线程后, 系统内部拥有一个该线程的句柄, 同时返回一个句柄给用户.
    所以, 用户关闭了句柄, 线程并不会退出, 直到线程执行结束了, 系统才会把系统的那一个句柄关闭, 这样, 线程对象的资源就别系统撤销了.
    展开全文
  • 什么是句柄句柄是一种指向指针的指针。我们知道,所谓指针是一种内存地址。应用程序启动后,组成这个程序的各对象是住留在内存的。如果简单地理解,似乎我们只要获知这个内存的首地址,那么就可以随时用这个地址...


    线程、线程句柄、线程ID_夜空_新浪博客  

    http://blog.sina.com.cn/s/blog_4e0494e00100jig5.html


    什么是句柄:
           句柄是一种指向指针的指针。我们知道,所谓指针是一种内存地址。应用程序启动后,组成这个程序的各对象是住留在内存的。如果简单地理解,似乎我们只要获知这个内存的首地址,那么就可以随时用这个地址访问对象。但是,如果您真的这样认为,那么您就大错特错了。我们知道,Windows是一个以虚拟内存为基础的操作系统。在这种系统环境下,Windows内存管理器经常在内存中来回移动对象,依此来满足各种应用程序的内存需要。对象被移动意味着它的地址变化了。如果地址总是如此变化,我们该到哪里去找该对象呢?
           为了解决这个问题,Windows操作系统为各应用程序腾出一些内存储地址,用来专门登记各应用对象在内存中的地址变化,而这个地址(存储单元的位置)本身是不变的。Windows内存管理器在移动对象在内存中的位置后,把对象新的地址告知这个句柄地址来保存。这样我们只需记住这个句柄地址就可以间接地知道对象具体在内存中的哪个位置。这个地址是在对象装载(Load)时由系统分配给的,当系统卸载时(Unload)又释放给系统。
           句柄地址(稳定)→记载着对象在内存中的地址→对象在内存中的地址(不稳定)→实际对象
    线程与线程句柄的关系:

           句柄可以认为是系统对资源(如线程)的分配的一个编号。关闭这个编号,对于不同的资源,效果不尽相同。对于线程来说,关闭这个编号并不意味着终止线程,只是之后很难再操纵这个线程。
    线程句柄与线程ID的区别:
    CreateThread() API 用于创建线程。 API 返回同时线程句柄和线程标识符 (ID)。线程句柄有完全访问权创建线程对象。 运行线程时线程 ID 唯一标识线程在系统级别。
    ●ID是在Windows系统范围内唯一标示Thread的。     
    ●Handle是用来操作Thread的,可以有多个,每个HANDLE可以有不同的操作权限,在不同进程OpenThread得到的值不一样。   
    ●线程的ID是系统全局的,其HANDLE是进程局部的.

    ●此ID只在线程的生存期内有效。

    ●HANDLE是os和client之间用来操作进程和线程一个桥梁,os有一张维护HANDLE的表单,里面大概放置了  HANDLE的引用计数和有关的属性,HANDLE是os标识进程和线程的东西,但是用户也可以用这个来标识进程和线程,对其操作;而ID是os用来标识进程和线程的,并且是全局唯一的,  但用户可以通过这个ID获得进程线程的HANDLE,多次得到的HANDLE并不一定是一样的.HANDLE是内核对象,而ID好像不是,并没有专门创建ID的函数.

    ●ID是CreateThread时操作系统自动生成的。

    ●线程的句柄和id是不同的。  
      在windows系统中,线程的id是唯一对应的,也就是说,如果两个线程返回相同的id,则他们必然是同一线程,反之一定是不同的线程。而线程的句柄并不是线程的唯一标识,线程的句柄只是用来访问该线程的的一个32位值,尽管相同的句柄一定标识同一线程,但同一线程可能拥有两个打开的句柄,因此,不能用句柄来区分两个线程是否是同一线程。
    线程终止运行时发生的操作
      当线程终止运行时,会发生下列操作:
      • 线程拥有的所有用户对象均被释放。在 Windows 中,大多数对象是由包含创建这些对象的线程的进程拥有的。但是一个线程拥有两个用户对象,即窗口和挂钩。当线程终止运行时,系统会自动撤消任何窗口,并且卸载线程创建的或安装的任何挂钩。其他对象只有在拥有线程的进程终止运行时才被撤消。
      • 线程的退出代码从 STILL_ACTIVE 改为传递给 ExitThread 或 TerminateThread 的代码。
      • 线程内核对象的状态变为已通知。
      • 如果线程是进程中最后一个活动线程,系统也将进程视为已经终止运行。
      • 线程内核对象的使用计数递减 1。
      当一个线程终止运行时,在与它相关联的线程内核对象的所有未结束的引用关闭之前,该内核对象不会自动被释放。
      一旦线程不再运行,系统中就没有别的线程能够处理该线程的句柄。然而别的线程可以调用 GetExitcodeThread 来检查由 hThread 标识的线程是否已经终止运行。如果它已经终止运行,则确定它的退出代码:
      BOOL GetExitCodeThread(HANDLE hThread, PDOWRD pdwExitCode);
      退出代码的值在 pdwExitCode 指向的 DWORD 中返回。如果调用 GetExitCodeThread 时线程尚未终止运行,该函数就用 STILL_ACTIVE 标识符(定义为 0x103)填入 DWORD。如果该函数运行成功,便返回 TRUE。

        线程退出的时候内核对象就会被激发, WaitForSingleObject()为堵塞函数,等待线程的内核对象被激发。所以终止线程并释放句柄对象的顺序是:TerminateThread()-->WaitForSingleObject()-->CloseHandle().

    线程、线程句柄、线程ID的生成和消失

    hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, &dwId);

    //至此,新线程,线程句柄,线程ID产生

    TerminateThread(hThread, 0);
    //至此,线程ID,线程句柄都依然存在

    WaitForSingleObject(hThread, INFINITE);

    //至此,线程本身和线程ID消失

    CloseHandle(hThread);
    //至此,线程句柄消失


    展开全文
  • 线程 与 线程句柄 (refer to: http://blog.csdn.net/titan_koa/article/details/2439027) 1. 线程和线程句柄(Handle)不是一个东西,线程是在cpu上运行的.....(说不清楚了),线程句柄是一个内核对象。我们...

    线程 与 线程句柄

    (refer to: http://blog.csdn.net/titan_koa/article/details/2439027)
    1. 线程和线程句柄(Handle)不是一个东西,线程是在cpu上运行的.....(说不清楚了),线程句柄是一个内核对象。我们可以通过句柄来操作线程,但是线程的生命周期和线程句柄的生命周期不一样的。线程的生命周期就是线程函数从开始执行到return,线程句柄的生命周期是从CreateThread返回到你CloseHandle()。

    2. 所有的内核对象(包括线程Handle)都是系统资源,用了要还的,也就是说用完后一定要closehandle关闭之,如果不这么做,你系统的句柄资源很快就用光了。

    3. 如果你CreateThread以后需要对这个线程做一些操作,比如改变优先级,被其他线程等待,强制TermateThread等,就要保存这个句柄,使用完了再CloseHandle。如果你开了一个线程,而不需要对它进行如何干预,CreateThread后直接CloseHandle就行了。所以CloseHandel(ThreadHandle );只是关闭了一个线程句柄对象,表示我不再使用该句柄,即不对这个句柄对应的线程做任何干预了。并没有结束线程。如果你觉得多了一个变量,也可以写为:CloseHandel(CreateThread(NULL,0,.....));

    4. CloseHandle的功能是关闭一个打开的对象句柄,该对象句柄可以是线程句柄,也可以是进程、信号量等其他内核对象的句柄,而ExitThread的功能是终止一个线程,它所接受的参数是一个线程的退出码。 通过调用CloseHandle可以告知系统,已经完成了对某一内核对象的操作,该函数首先检查调用进程的句柄表,来确认进程是否对该句柄所指向的对象有访问权,如果句柄无效则返回FALSE,如果有效,系统将得到该内核对象的数据结构的地址,把结构中的使用计数成员减1,如果计数变为0,则将从内核中释放该内核对象。如果计数还未到0,就意味着还有其他的进程在使用这个内核对象,那么它就不会被释放。

    5. ExitThread是推荐使用的结束一个线程的方法,当调用该函数时,当前线程的栈被释放,然后线程终止,相对于TerminateThread函数来说,这样做能够更好地完成附加在该线程上的DLL的清除

    进程句柄 与 进程ID

    进程是一种内核对象,每个内核对象实际上是由内核分配的一块内存,而且只能由内核来访问。这一内存块是一个数据结构,它的成员包含有关于该对象的信息。因为内核对象只能由内核访问,所以应用程序不可能在内存中定位这些数据结构和直接改变它们的内容。为了达到操纵这些内核对象的目的,win32API以良好的方式提供了一组操纵这些结构的函数,对内核对象的访问总是通过这些函数.

    当调用创建内核对象的函数时,函数返回一个标志该对象的句柄,它是一个32位的数值,可以被进程中的任意线程使用,可以把它传给各种WIN32函数,这样系统就知道想要操纵的是哪一个内核对象。

    ------------------------------------------------------------------------------------------------------------------
    creating a new process causes the system to create a process kernel object 
    and a thread kernel object. At creation time, the system gives each object 
    an initial usage count of 1. Then, just before CreateProcess returns, the 
    function opens the process object and the thread object and places the 
    process-relative handles for each in the hProcess and hThread members of 
    the PROCESS_INFORMATION structure. When CreateProcess opens these objects 
    internally, the usage count for each becomes 2.

    当创建进程后, CreateThread返回的PROCESS_INFORMATION structure中进程, 线程的引用计数不是1,而是2。
    创建新的进程后,记数初始化为1,而函数需要返回进程内核对象的句柄,相当于打开一次新创建的类核对象,记数再加1.
    ------------------------------------------------------------------------------------------------------------------

    而进程被创建时,系统会赋给它一个唯一的标识符,就是进程ID。进程ID是唯一的, 系统中运行的其他进程不会有相同的ID值. ,进程句柄被打开一次就有一个,同一个进程ID可以有多个进程句柄.

    获取当前进线程ID可使用API: GetCurrentProcessId(), GetCurrentThreadId()

    进程句柄->进程ID

    (refer to: http://www.usidcbbs.com/simple/?t5426.html)
    (1)用户态:在用户态下面比较容易,直接通过GetProcessId,传入进程句柄,就返回句柄对应的ID
    (2)内核态:在内核态下面,需要调用Undocument API ZwQueryInformationProcess,根据API名字,我们很容易能明白该API的目的,无非就是查询对应进程的一些其它信息,传入的参数为ProcessID,然后ProcessClass填入0,则返回进程的基本信息,其返回结构如下:

    复制代码
    typedef struct _PROCESS_BASIC_INFORMATION { 
        PVOID Reserved1; 
        PPEB PebBaseAddress; 
        PVOID Reserved2[2]; 
        ULONG_PTR UniqueProcessId; 
        PVOID Reserved3; 
    } PROCESS_BASIC_INFORMATION;
    复制代码

    其中,UnicodeProcessId就是句柄对应的进程ID.

    另外, 最后一个字段Reserved3就是Parent进程的ID. 关于如何"通过子进程ID, 获取父进程的ID,然后通过父进程ID来获取父进程Handle"的方法,可参考: (http://blog.163.com/wangninghaha@126/blog/static/204531052012827111415247/)

    进程ID->进程句柄

    这个过程就比较直接了,无论在用户态还是在内核态,打开进程,就能够获取进程的句柄了.

    HANDLE GetProcessHandle(int nID)  //通过进程ID获取进程句柄
    {
        return OpenProcess(PROCESS_ALL_ACCESS, FALSE, nID);
    }

    进程名->进程句柄

    复制代码
    HANDLE GetProcessHandle(LPCTSTR pName)  //通过进程名获取进程句柄
    {
        HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        if (INVALID_HANDLE_VALUE == hSnapshot) {
            return NULL;
        }
        PROCESSENTRY32 pe = { sizeof(pe) };
        BOOL fOk;
        for (fOk = Process32First(hSnapshot, &pe); fOk; fOk = Process32Next(hSnapshot, &pe)) {
            if (!_tcscmp(pe.szExeFile, pName)) {
                CloseHandle(hSnapshot);
                return GetProcessHandle(pe.th32ProcessID);
            }
        }
        return NULL;
    }
    复制代码

    伪句柄 pseudohandle 和句柄的转换

    (refer to:

    1. http://www.cnblogs.com/Kernone/archive/2009/08/18/1549286.html

    2. http://blog.sina.com.cn/s/blog_4b226b9201011bu6.html )

    在使用很多函数的时候,我们都需要获得一个对象的句柄,而某些函数,如GetCurrentProcess() / GetCurrentThread() 返回的是伪句柄 pseudohandle。

    所谓伪句柄,即指向当前线程或者进程的句柄,并不是真正意义上的句柄。它并不创建句柄,同时也不增加引用计数,因次调用CloseHandle()不作任何处理。

    它本身就只指向调用它的主调进程或线程。会因为调用者的不同而改变,比如:调用者A使用一个伪句柄,这个句柄指向调用者A,而调用者A将该句柄传递给调用者X,则这个句柄就指向调用者X。

    进程的伪句柄总是0xffffffff,而线程的伪句柄总是0xfffffffe。

    通过使用DuplicateHandle这个强大的函数,可以将伪句柄转换为真正的句柄。

    函数原型: 

    复制代码
    BOOL DuplicateHandle(
      HANDLE hSourceProcessHandle,  // handle to source process
      HANDLE hSourceHandle,         // handle to duplicate
      HANDLE hTargetProcessHandle,  // handle to target process
      LPHANDLE lpTargetHandle,      // duplicate handle
      DWORD dwDesiredAccess,        // requested access
      BOOL bInheritHandle,          // handle inheritance option
      DWORD dwOptions               // optional actions
    );
    复制代码

    参数:

    1> hSourceProcessHandle:拥有待复制HANDLE的进程句柄;

    2> hSourceHandle:待复制的HANDLE;

    3> hTargetProcessHandle:接收复制后HANDLE的进程句柄;

    4> lpTargetHandle:指向 存储复制后HANDLE的 地址;如果取值为NULL,函数复制句柄,但不会返回复制后的句柄

    5> dwDesiredAccess:指明对新HANDLE的访问权限;

    6> bInheritHandle:指明新HANDLE是否可被继承;

    7> dwOptions:指明可选的行为;可以取值为0,也可以取如下值的任意结合:

    DUPLICATE_CLOSE_SOURCE:关闭掉原HANDLE;

    DUPLICATE_SAME_ACCESS:忽略dwDesiredAccess指明的对新HANDLE的访问权限;新句柄具有同原句柄相同的访问权限;

    返回值:

    如果成功,返回非零值,即TRUE;

    如果失败,返回0,即FALSE;(可以调用GetLastError()获取详细错误信息)

    注意:

    1. 原句柄和复制后的句柄是类似引用的关系,两者指向同一内核对象,对原句柄或复制后的句柄,其中任何一个的改变,都会导致另外一个的改变

    2. DuplicateHandle()会递增特定对象的使用计数,因此当完成对复制对象句柄的使用时,应该调用CloseHandle()关闭句柄,从而递减对象的使用计数;

    3. lpTargetHandle所指向的句柄跟CreateThread()时创建的句柄不一定相同

    示例:

    HANDLE hPseudoThread=GetCurrentThread();  
    HANDLE hProcess=GetCurrentProcess();  
    HANDLE hRealThread=NULL;  
    DuplicateHandle(hProcess, hPseudoThread, hProcess, &hRealThread, 0, false, 0);  

    调试内容:

    hPseudoThread: 0xfffffffe void *

    hProcess: 0xffffffff void *

    hRealThread: 0x00000ed8 void *

    展开全文
  • hThread:线程句柄 返回值:成功返回线程ID,否则返回零值 */ DWORD GetThreadIdEx(HANDLE hThread) { THREAD_BASIC_INFORMATION tbi; DWORD dwReturnedSize; if(::ZwQueryInformationThread(hThread,...
  • API 返回同时线程句柄和线程标识符 (ID)。 线程句柄有完全访问权创建线程对象。 运行线程时线程 ID 唯一标识线程在系统级别。 ●ID是在Windows系统范围内唯一标示Thread的。  ●Handle是用来操作Thread的,可以...
  • 窗口句柄  在Windows中,句柄是一个系统内部数据结构的引用。例如,当你操作一个窗口,或说是一个Delphi窗体时,系统会给你一个该窗口的句柄,系统会通知你:你正在操作142号窗口,就此,你的应用程序就能要求系统...
  • 线程ID线程句柄的关系

    千次阅读 2015-05-20 10:13:08
    很多人不了解线程ID的作用,现进行一定的解释,如有疑问可以留言。本文区分了线程ID线程句柄的作用,详细区别可以参考响应的操作系统方面的知识以及MFC相关的知识。
  • 线程ID线程句柄 区别 (转载) 以下几点是一些总结: ●CreateThread() API 用于创建线程。 API 返回同时线程句柄和线程标识符 (ID)。 线程句柄有完全访问权创建线程对象。 运行线程时线程 ID 唯一标识线程在系统...
  • API 返回同时线程句柄和线程标识符 (ID)。 线程句柄有完全访问权创建线程对象。 运行线程时线程 ID 唯一标识线程在系统级别。●ID是在Windows系统范围内唯一标示Thread的。 ●Handle是用来操作Thr
  • Windows中的线程ID线程句柄 1.什么是句柄? 本质是指针,在Win中句柄是指向指针的指针 typedef void *HANDLE; 一个应用程序,组成该程序的众多对象存在内存中 一般的理解是,只要获取了对象的内存地址便可以...
  • 什么是句柄:  句柄是一种指向指针的指针。我们知道,所谓指针是一种内存地址。应用程序启动后,组成这个程序的各对象是住留在内存的。如果简单地理解,似乎我们只要获知这个内存的首地址,那么就可以随时用这个...
  • API 返回同时线程句柄和线程标识符 (ID)。 线程句柄有完全访问权创建线程对象。 运行线程时线程 ID 唯一标识线程在系统级别。●ID是在Windows系统范围内唯一标示Thread的。 ●Handle是用来操作Thread的,可以有多...
  • 线程、线程句柄线程ID的关系

    千次阅读 2014-03-30 17:38:24
    什么是句柄:  句柄是一种指向指针的指针。我们知道,所谓指针是一种内存地址。应用程序启动后,组成这个程序的各对象是住留在内存的。如果简单地理解,似乎我们只要获知这个内存的首地址,那么就可以随时用这个...
  • 线程句柄和线程标识

    2017-11-08 12:40:07
    线程句柄线程ID的区别 HANDLE的引用计数和有关的属性,HANDLE是os标识进程和线程的东西,但是用户也可以用这个来标识进程和线程,对其操作 ●CreateThread() API 用于创建线程。 API 返回同时线程句柄,并...
  • 线程句柄HANDLE与线程ID的关系

    千次阅读 2019-04-29 17:39:57
    什么是句柄 句柄是一种指向指针的指针。我们知道,所谓指针是一种内存地址。应用程序启动后,组成这个程序的各对象是住留在内存的。如果简单地理解,似乎我们只要获知这个内存的首地址,那么就可以随时用这个地址...
  • 线程ID句柄的区别

    2012-04-20 21:44:20
    API 返回同时线程句柄和线程标识符 (ID)。 线程句柄有完全访问权创建线程对象。 运行线程时线程 ID 唯一标识线程在系统级别。 ●ID是在Windows系统范围内唯一标示Thread的。  ●Handle是用来操作Thread的,可以...
  • 我们采用GetWindowThreadProcessId来获取窗口线程ID.获取与指定窗口关联在一起的一....DLL命令 GetWindowThreadProcessId, 整数型, "user32", "GetWindowThreadProcessId", 公开, 窗口_句柄线程ID 获取与指定窗口关联

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 56,735
精华内容 22,694
关键字:

线程id线程句柄