创建线程
什么是线程
- 线程是附属在进程上的执行实体,是代码的执行流程。
- 一个进程可以包含多个线程,但是一个进程至少要包含一个线程。(进程是空间上的概念,线程是时间上的概念)
- 单核CPU可以执行多线程程序
有几个线程就表示着有几个代码在执行,但是它们并不一定是同时执行,例如单核的CPU情况下是不存在多线程的,线程的执行是有时间顺序的,但是CPU切换的非常快,所以给我们的感觉和多核CPU没有什么区别。
创建线程CreateThread
Creates a thread to execute within the virtual address space of the calling process.
To create a thread that runs in the virtual address space of another process, use the CreateRemoteThread function.
HANDLE WINAPI CreateThread( //返回值是线程句柄
LPSECURITY_ATTRIBUTES lpThreadAttributes,//SD安全属性,包含安全描述符
SIZE_T dwStackSize, //初始堆栈,不填写系统给一个默认的堆栈
LPTHREAD_START_ROUTINE lpStartAddress,//线程执行的函数代码
LPVOID lpParameter,//线程需要的参数
DWORD dwCreationFlags,//标识,也可以以挂起形式创建线程
LPDWORD lpThreadId//返回当前线程ID
);
#include "stdafx.h"
#include <windows.h>
//线程执行的函数有语法要求,参考MSDN Library
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
//要执行的代码
for (int i=0; i<100;i++)
{
Sleep(500);
printf("++++++++ %d \n",i);
}
return 0;
}
int main(int argc, char* argv[])
{
//创建线程
CreateThread(NULL,NULL,ThreadProc,NULL,0,NULL);
//要执行的代码
for (int i=0;i<100;i++)
{
Sleep(500);
printf("------ %d \n",i);
}
printf("Hello World!\n");
return 0;
}
线程间不会相互配合,而是各自执行自己的,如果想要配合就需要了解线程通信,这个后面会学习到。
向线程函数传递参数
向线程传递参数,如下图所示,我们想要自定义线程执行for循环的次数,将n传递进去,这时候需要注意参数传递到线程参数时在堆栈中存在,并且传递的时候需要强制转换一下:
#include "stdafx.h"
#include <windows.h>
//线程执行的函数有语法要求,参考MSDN Library
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
int* p=(int*)lpParameter;
//要执行的代码
for (int i=0;i<*p;i++)
{
Sleep(500);
printf("+++++++ %d\n",i);
}
return 0;
}
int main(int argc, char* argv[])
{
int n = 10;
//创建线程
CreateThread(NULL,NULL,ThreadProc,(LPVOID )&n,0,NULL);
//要执行的代码
for (int i = 0 ; i<100;i++)
{
Sleep(500);
printf("-------------%d \n",i);
}
}
线程控制
Sleep函数
Sleep函数是让当前执行到本函数时延迟指定的毫秒数之后再向下走例如
for(int i = 0; i<100;i++)
{
Sleep(500);
printf("-----%d\n",i);
}
SuspendThread函数
SuspendThread函数用于暂停(挂起)某个线程,当暂停后该线程不会占用CPU,其语法格式很简单,只需要传入一个线程句柄即可:
DWORD SuspendThread(
HANDLE hThread //handle to thread
);
ResumeThread函数
ResumeThread函数用于恢复被暂停(挂起)某个线程,其语法格式也很简单,只需要传入一个线程句柄即可:
DWORD ResumeThread(
HANDLE hThread //handle to thread
);
需要注意的是,挂起几次就要恢复几次
SuspendThread(hThread);
SuspendThread(hThread);
ResumeThread(hThread);
ResumeThread(hThread);
等待线程结束
WaitForSingleObject函数
WaitForSingleObject函数用于等待一个内核对象状态发生变更,那也就是执行结束之后其语法格式如下
DWORD WaitForSingleObject(
HANDLE hHandle, //handle to object 句柄
DWORD dwMilliseconds //time-out interval 等待超时时间
);
如果你想一直等待的话,可以将第二参数的值设置为INFINITE
HANDLE hThread
hThread = CreateThread(NULL,NULL,ThreadProc,NULL,0.NULL);
WaitForSingleObject(hThread,INFINITE);
printf("OK..");
WaitForMultipleObjdects函数
WaitForMultipleObjects函数与WaitForSingleObject函数作用是一样的,只不过它可以等待多个内核对象的状态发生变更,其语法格式如下
DWORD WaitForMultipleObjects(
DWORD nCount,//number of handles in array 内核对象的数量
CONST HANDLE* lpHandles,//object-handle array 内核对象的句柄数组
BOOL bWaitAll,//wait option 等待模式
DWORD dwMilliseconds //time-out interval 等待超时时间(毫秒)
)
等待模式的值是布尔类型,一个是TRUE,一个是FALSE,TRUE就是等待所有对象的所有状态发生变更,FALSE则是等待任意一个对象的状态发生变更。
HANDLE hThread[2];
hThread[0] = CreateThread(NULL, NULL, ThreadProc, NULL, 0, NULL);
hThread[1] = CreateThread(NULL, NULL, ThreadProc, NULL, 0, NULL);
WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
GetExitCodeThread函数
线程函数会有一个返回值(DWORD),这个返回值可以根据你的需求进行返回,而我们需要如何获取这个返回值呢?这时候就可以使用GetExitCodeThread函数,其语法格式如下:
BOOL GetExitCodeThread(
HANDLE hThread, //handle to the thread
LPDWORD lpExitCode // termination status
);
根据MSDN Library我们可以知道的参数分别是线程句柄,而另一个则是out类型参数,这种类型可以理解为GetExitCodeThread函数的返回结果
HANDLE hThread;
hThread = CreateThread(NULL,NULL,ThreadProc,NULL,0,NULL);
WaitForSingleObject(hThread,INFINITE);
DWORD exitCode;
GetExitCodeThread(hThread,&exitCode);
printf("Exit Code: %d\n",exitCode);
设置、获取线程上下文
线程上下文是指某一时间点CPU寄存器和程序计数器的内容,如果想要设置、获取线程上下文就需要先将线程挂起
GetThreadContext函数
GetThreadContext函数用于获取线程上下文,其语法格式如下:
BOOL GetThreadContext(
HANDLE hThread, //handle to thread 句柄
LPCONTEXT lpContext //context structure
);
第一个参数就是线程句柄,这个很好理解,重点是第二个参数,其是一个CONTEXT结构体,该结构体包含指定线程的上下文,其ContextFlags成员的值指定了要设置线程上下文的哪些部分。
当我们将CONTEXT结构体的ContextFlags成员的值设置为CONTEXT_INTEGER时则可以获取edi、esi、ebx、edx、ecx、eax这些寄存器的值:
HANDLE hThread;
hThread = CreateThread(NULL,NULL,ThreadProc,NULL,0,NULL);
SuspendThread(hThread);
CONTEXT c;
c.ContextFlags = CONTEXT_INTEGER;
GetThreadContext(hThread,&c);
printf("%x %x \n",c.Eax,c.Ecx);
SetThreadContext函数
GetThreadContext函数是个设置修改修改线程上下文,其语法格式如下:
BOOL SetThreadContext(
HANDLE hThread, //handle to thread
CONST CONTEXT* lpContext //context structure
)
我们可以尝试修改Eax,然后再获取: