创建线程

什么是线程

  • 线程是附属在进程上的执行实体,是代码的执行流程。
  • 一个进程可以包含多个线程,但是一个进程至少要包含一个线程。(进程是空间上的概念,线程是时间上的概念)
  • 单核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,然后再获取:


posted @ 2022-04-04 21:45  不会笑的孩子  阅读(73)  评论(0编辑  收藏  举报