zoukankan      html  css  js  c++  java
  • Win32编程之控制线程

    0x01. 如何让线程停下来

    让自己停下来:
    Sleep() 函数
    当程序执行到某段代码的时候可以使用sleep() 函数进行暂停
    使用sleep()函数挂起的时候会自动恢复过来的

    让别人停下来:
    SuspendThread() 函数
    使用这个函数挂起,也就是阻塞的时候,必须使用ResumeThread()函数来恢复

    线程恢复:
    ResumeThread() 函数

    我们先讲Sleep函数:
    以上章代码为例子:

    #include <stdio.h>
    #include <windows.h>
    
    
    DWORD WINAPI ThreadProc(LPVOID lpParameter)
    {
    	for (int i = 0; i < 100; i++)
    	{
    		Sleep(500);
    		printf("++++++++++++++ %d 
    ", i);
    
    	}
    
    	return 0;
    }
    
    
    int main()
    {
    	int n;
    	n = 10;
    	HANDLE hThread =  CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
    
    	Sleep(5000);  //main函数这个线程睡眠五秒
    	SuspendThread(hThread); //挂起线程,也就是阻塞状态
    	Sleep(5000);  //再睡眠5秒
    	ResumeThread(hThread); //恢复线程
    
    	CloseHandle(hThread); //关闭线程句柄
    	getchar();
    	return 0;
    }
    
    
    

    注意:关闭线程的话要放在下面,不然无法控制。因为线程是时间型,你执行之后关闭句柄,线程还是在执行,但是执行完代码之后就关闭了

    然而需要注意的是线程挂起几次,就需要使用函数ResumeThread()恢复

    
    int main()
    {
    	int n;
    	n = 10;
    	HANDLE hThread =  CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
    
    	Sleep(5000);  //main函数这个线程睡眠五秒
    	SuspendThread(hThread); //挂起线程,也就是阻塞状态
            SuspendThread(hThread);
    	Sleep(5000);
    	ResumeThread(hThread);
            ResumeThread(hThread);
    
    	CloseHandle(hThread);
    	getchar();
    	return 0;
    }
    

    sleep的话睡眠之后是会自动启动,使用SuspendThread()函数是需要另外ResumeThread()恢复的

    当线程执行做另外一件事的时候,怎么知道线程执行没执行完毕呢?
    需要用到两个函数

    1、WaitForSingleObject()
    2、WaitForMultipleObjects()

    第一个参数是句柄
    dwMilliseconds : 等待的最长时间,时间终了,即使handle尚未成为激发状态,此函数还是要返回。
    此值可以是0(代表立即返回),也可以是INFINITE(代表无穷等待)。

    可以看出它一直在不断的跑,直到线程发生变化,这个函数就往下走了,我们接下来可以看看是不是把线程都执行完了,才打印那段话

    #include <stdio.h>
    #include <windows.h>
    
    
    DWORD WINAPI ThreadProc(LPVOID lpParameter)
    {
    	for (int i = 0; i < 30; i++)
    	{
    		Sleep(50);
    		printf("++++++++++++++ %d 
    ", i);
    
    	}
    
    	return 0;
    }
    
    
    int main()
    {
    	int n;
    	n = 10;
    	HANDLE hThread[2];
    	hThread[0] = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
    	hThread[1] = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
    	
    	WaitForSingleObject(hThread, INFINITE);
    	printf("线程执行完毕 
    ");
    
    
    	//Sleep(5000);  //main函数这个线程睡眠五秒
    	//SuspendThread(hThread); //挂起线程,也就是阻塞状态
    	//Sleep(5000);
    	//ResumeThread(hThread);
    	 
    	CloseHandle(hThread);
    	getchar();
    	return 0;
    }
    
    
    

    再次修改一下代码,再创建一个线程,线程是可以同一段代码,但是它们是两个堆栈,互相没关系的
    当这两个线程执行完毕的时候,我们再执行 线程执行完毕 这段代码
    那么这边就用到了新的 API,也就是WaitForMultipleObjects()

    DWORD WaitForMultipleObjects(
      DWORD        nCount,   //等几个内核对象
      const HANDLE *lpHandles, //内核对象数组
      BOOL         bWaitAll,   //等待模式
      DWORD        dwMilliseconds  //等待时间,想一直等待的话 INFINITE
    );
    

    等待模式是什么?
    可以指定所有等待对象状态都发生变更的时候才返回,也可以指定任何一个对象发生变更就返回

    True:这个函数当所有对象都发生改变,才返回

    补充

    DWORD WINAPI ThreadProc(LPVOID lpParameter)
    {
    	for (int i = 0; i < 30; i++)
    	{
    		Sleep(50);
    		printf("++++++++++++++ %d 
    ", i);
    
    	}
    
    	return 0;
    }
    

    比如说这边,为什么会有个DWORD,因为我们可以看这个线程返回值,执行成功的话返回什么,执行失败的话返回什么
    具体可以根据项目需求来定

    这时候可以使用另外一个函数,GetExitCodeThread()函数

    第一个就是Handle,也就是句柄,我们把刚刚的两个线程句柄放进去就行了
    第二个参数就是接收的参数,IN 是输入;_OUT_是输出,这时候我们可以定义两个变量来接收这个参数
    完整代码如下:

    #include <stdio.h>
    #include <windows.h>
    
    
    DWORD WINAPI ThreadProc(LPVOID lpParameter)
    {
    	for (int i = 0; i < 30; i++)
    	{
    		Sleep(50);
    		printf("++++++++++++++ %d 
    ", i);
    
    	}
    
    	return 0;
    }
    
    DWORD WINAPI ThreadProc2(LPVOID lpParameter)
    {
    	for (int i = 0; i < 30; i++)
    	{
    		Sleep(50);
    		printf("++++++++++++++ %d 
    ", i);
    
    	}
    
    	return 1;
    }
    int main()
    {
    	HANDLE arrhThread[2];
    	DWORD dwResult1;
    	DWORD dwResult2;
    
    	arrhThread[0] = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
    	arrhThread[1] = CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL);
    	
    	WaitForMultipleObjects(2, arrhThread, TRUE, INFINITE);
    	printf("线程执行完毕 
    ");
    	GetExitCodeThread(arrhThread[0], &dwResult1);
    	GetExitCodeThread(arrhThread[1], &dwResult2);
    
    	CloseHandle(arrhThread[0]);
    	CloseHandle(arrhThread[1]);
    	getchar();
    	return 0;
    }
    

    小Tips:
    如果你当前的电脑只有一个核,当你的A线程跑一半,切换到了B线程,A线程怎么办?

    因为程序跑的时候需要一堆寄存器,比如eax,ebx,ecx,edx ,那A线程要不要保留呢?

    其实丢不了,每个线程都有一个结构体,当自己被切换的时候,会把当前运行情况,寄存器里面的值存到结构体
    这个结构体叫CONTEXT;

    然后我们跟进去,可以发现一大堆的寄存器

    接下来我们就可以写代码,体会一下CONTEXT;

    因为这个结构体里面好多寄存器,所以微软给了个方便的方法,那就是一段一段的获取

    比如我们想要这一段的寄存器
    context.ContextFlags = CONTEXT_INTEGER;
    我们可以这样写,就更方便了,想看其他段就把 CONTEXT_INTEGER 改成那个段对应的参数

    设置、获取线程上下文

    通过上面我们已经赋好值了,代码如下:

    #include <stdio.h>
    #include <windows.h>
    
    
    DWORD WINAPI ThreadProc(LPVOID lpParameter)
    {
    	for (int i = 0; i < 30; i++)
    	{
    		Sleep(50);
    		printf("++++++++++++++ %d 
    ", i);
    
    	}
    
    	return 0;
    }
    
    DWORD WINAPI ThreadProc2(LPVOID lpParameter)
    {
    	for (int i = 0; i < 30; i++)
    	{
    		Sleep(50);
    		printf("++++++++++++++ %d 
    ", i);
    
    	}
    
    	return 1;
    }
    int main()
    {
    	HANDLE arrhThread[2];
    	DWORD dwResult1;
    	DWORD dwResult2;
    
    	arrhThread[0] = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
    	arrhThread[1] = CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL);
    	
    	SuspendThread(arrhThread[0]);
    	CONTEXT context;
    	context.ContextFlags = CONTEXT_INTEGER;
    
    	CloseHandle(arrhThread[0]);
    	CloseHandle(arrhThread[1]);
    	getchar();
    	return 0;
    }
    

    我们需要用到新的API来获取上下文
    1、GetThreadContext() //获取值

    BOOL WINAPI GetThreadContext(
        _In_ HANDLE hThread,           //线程句柄
        _Inout_ LPCONTEXT lpContext    //指针
        );
    

    2、SetThreadContext() //设置修改值

    BOOL WINAPI SetThreadContext(
        _In_ HANDLE hThread,
        _In_ CONST CONTEXT * lpContext
        );
    
    int main()
    {
    	HANDLE arrhThread[2];
    	DWORD dwResult1;
    	DWORD dwResult2;
    
    	arrhThread[0] = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
    	//arrhThread[1] = CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL);
    	
    	SuspendThread(arrhThread[0]);
    	CONTEXT context;
    	context.ContextFlags = CONTEXT_INTEGER;
    	GetThreadContext(arrhThread[0], &context);
    	printf("%#x %#x
    ", context.Eax, context.Ebx);
    	ResumeThread(arrhThread[0]);
    
    	CloseHandle(arrhThread[0]);
    	//CloseHandle(arrhThread[1]);
    	getchar();
    	return 0;
    }
    

    我们获取的话也可以修改,但是我们就打印一下就可以了

    成功得到了寄存器的值

    这时候就可以知道为什么一个核也可以跑那么快了,因为线程被切换会把寄存器数据存入结构体,当个再次启用的时候
    再把结构体的值取出来,放到当前CPU的寄存器里

    总结:

  • 相关阅读:
    ThinkPHP部署
    Linux下的vim常用操作
    Linux常用命令
    PHP中常用操作文件的方法
    PHP中的错误处理机制
    06 webpack4.0学习笔记——配置文件_sass-loader使用
    05 webpack4.0学习笔记——配置文件_babel-loader使用
    04 webpack4.0学习笔记——配置文件_url-loader使用
    03 webpack4.0学习笔记——配置文件_入口出口
    02 webpack4.0学习笔记——安装、基本命令
  • 原文地址:https://www.cnblogs.com/0x7e/p/13827635.html
Copyright © 2011-2022 走看看