Windows编程-线程

概念

进程是操作系统分配资源的单位,线程是执行任务的单元。一个进程至少有一个线程。线程还可以再创建线程,不过每一个线程都是独立的执行单元,相互间没有从属关系

创建线程-CreateThread

每个程序都会有一个主线程也就是每个程序的最开始跑的线程。

HANDLE CreateThread(
LPSECURITY_ATTRIBUTES   lpThreadAttributes,
SIZE_T                 dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
__drv_aliasesMem LPVOID lpParameter,
DWORD                   dwCreationFlags,
LPDWORD                 lpThreadId
);

参数:

第一个参数 安全描述符(Windows内核层的都有这个)

第二个参数 栈的大小

第三个参数 表示创建的线程的执行起始地址

必须用指定的回调函数模板来创建

第四个参数 线程执行的函数地址的参数

第五个参数 控制线程创建的标志。也就是创建线程后线程的执行状态

第六个参数 指向接收线程标识符的变量的指针。如果此参数为 NULL,则不返回线程标识符。

返回值:

失败返回NULL

成功返回新线程的句柄

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
printf("hello thread\n");
return 0;
}
void Create_My_Thread()
{
HANDLE hThread = CreateThread(
NULL,
NULL,
ThreadProc,
0,
0,
NULL
);
}

线程状态

线程有激发态(有信号状态)和非激发态(无信号状态)之分,正在运行的线程处于非激发态,线程结束就会变为激发态

WaitForSingleObject就是等待线程处于激发态,也就是等待某个线程结束,也可以用于别的对象,用来等待各种事情。

线程相关API

CreateThread创建线程
OpenThread 打开线程
ExitThread 退出线程
TerminateThread 指定线程结束
SuspendThread 暂停线程
ResumeThread 恢复线程
GetCurrentProcess 获取当前进程的句柄(伪句柄)
GetCurrentThread 获取当前线程的句柄(伪句柄)
GetCurrentProcessID 获取当前进程ID
GetCurrentThreadID 获取当前线程的ID

遍历线程

创建快照-CreateToolhelp32Snapshot

拍摄指定进程以及这些进程使用的堆,模块和线程的快照。

这个函数可以编程进程,堆,线程等等模块,这里用来遍历线程

HANDLE CreateToolhelp32Snapshot(
DWORD dwFlags,
DWORD th32ProcessID
);

第一个参数用来表示要遍历的东西

第二个参数是一个结构体用来存储遍历的信息

 

这里用于遍历线程所以第一个参数采用TH32CS_SNAPTHREAD

在快照中包括系统中的所有线程。要枚举线程,请参见 Thread32First。
要标识属于特定进程的线程,请在枚举线程时将其进程标识符与THREADENTRY32结构的th32OwnerProcessID成员 进行比较。

第二个参数是进程的ID第二个参数只有在遍历线程,模块和堆的情况下需要,因为线程模块和堆都是进程里面包含的。但是获取线程只能获取系统的所有线程所以这里填为0就好了。这里如果要指定某进程的话,需要在th32ProcessID结构体中来指定

CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,0)

遍历快照

这里和遍历进程一样,需要用到Thread32First和Thread32Next函数

void Ergodic_Thread()
{
THREADENTRY32 lpte;
lpte.dwSize = sizeof(lpte);
HANDLE hThreadEr = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,0);
Thread32First(hThreadEr, &lpte);
do
{
/*
BOOL Thread32First(
HANDLE         hSnapshot,
LPTHREADENTRY32 lpte
);*/
printf("Thread ID = %ld\n", lpte.th32ThreadID);
} while (Thread32Next(hThreadEr, &lpte));

}

如果要指定遍历进程的ID的话在lpte结构体里面进行比较

void Ergodic_Appoint_Thread(DWORD pro_ID)
{
THREADENTRY32 lpte;
lpte.dwSize = sizeof(lpte);
HANDLE hThreadEr = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
Thread32First(hThreadEr, &lpte);
do
{
/*
BOOL Thread32First(
HANDLE         hSnapshot,
LPTHREADENTRY32 lpte
);*/
if (lpte.th32OwnerProcessID == pro_ID)
{
printf("Thread ID = %ld\n", lpte.th32ThreadID);
}
else {
continue;
}
} while (Thread32Next(hThreadEr, &lpte));
}

获得线程句柄-OpenThread

HANDLE OpenThread(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwThreadId
);

第一个参数表示这个句柄对线程的访问权限

第二个参数表示该线程创建的进程是否继承该句柄

第三个参数表示要打开的线程的ID

暂停线程-SuspendThread

DWORD SuspendThread(
HANDLE hThread
);

直接输入要暂停挂起的线程的句柄就好

恢复进程-ResumeThread

DWORD ResumeThread(
HANDLE hThread
);

其实就是恢复暂停的线程

获得当前线程/进程句柄-GetCurrentThread

这里获得的其实是一个伪句柄。这个只能在自己的进程里面用,在别的进程里面用不了。如果想要得真实的当前线程/进程的句柄则需要调用

DuplicateHandle来产生一个真正意义的句柄

关闭线程

主线程关闭后会自动关闭进程