Windows编程----线程管理
系统中,进程主要有两部分组成:进程内核对象和进程地址空间。操作系统通过进程内核对象来管理进程,进程地址空间用于维护进程所需的资源:如代码、全局变量、资源文件等。
那么线程也是有两部分组成:线程内核对象和线程堆栈。操作系统通过线程内核对象对线程进行管理,线程堆栈用于维护线程执行代码时需要的所有的函数参数和局部变量。
线程的开销远小于进程,所以在并发执行多个任务的时候,应该尽可能使用多线程 解决问题,而不是使用多进程解决问题。
创建线程
当exe程序启动的时候,操作系统会创建一个主线程,用于执行入口函数(main函数)。通过在主线程中调用对应的函数,可以创建更多的线程来执行任务。通过CreateThread函数可以创建一个线程。
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,//线程安全描述符
SIZE_T dwStackSize,// 初始堆栈大小,通常为 0,表示使用默认大小。
LPTHREAD_START_ROUTINE lpStartAddress,//多线程要执行的函数指针
__drv_aliasesMem LPVOID lpParameter,//传递给多线程的参数
DWORD dwCreationFlags,// 线程创建标志,通常为0,表示立即执行。CREATE_SUSPENDED表示创建线程之后,不立即调度。
LPDWORD lpThreadId //指向接收线程 ID 的变量的指针,通常为 NULL。
);
下面是同CreateThread创建多线程并执行一个函数的简单示例代码。
#include <iostream>
#include <Windows.h>
// 线程函数
DWORD WINAPI ThreadFunction(LPVOID lpParam)
{
while (true)
{
std::cout << "Hello, World!" << std::endl;
Sleep(1000); // 休眠1秒
}
return 0;
}
int main()
{ // 创建线程
HANDLE hThread = CreateThread(
NULL, // 默认安全属性
0, // 默认堆栈大小
ThreadFunction, // 线程函数
NULL, // 线程函数参数
0, // 默认创建标志
NULL); // 不需要线程ID
// 防止主线程立即退出
WaitForSingleObject(hThread, INFINITE);
// 关闭线程句柄
CloseHandle(hThread);
return 0;
}
终止线程
(1)、通过线程要执行的函数正常返回(建议使用该方法)。
(2)、通过调用ExitThread函数退出线程,一般不建议使用该函数。
(3)、通过调用TerminateThread函数退出线程,一般也不建议使用该方法。
(4)、直接结束进程,可以间接终止线程的执行。
创建线程二
上面代码,我们使用CreateThread函数创建了线程,该函数是Windows的一个函数,并不是C/C++库中提供的函数。我们可以使用_beginthreadex函数创建线程,通过_endthreadex函数结束线程。这是我非常推荐和常用的创建线程的方式。_beginthreadex的函数原型如下,和CreateThread函数原型相差不是很大。
uintptr_t _beginthreadex(
void* _Security,//线程安全描述符
unsigned _StackSize,//线程的初始堆栈大小,通常为 0,表示使用默认大小。
_beginthreadex_proc_type _StartAddress,//线程函数的指针,即线程的入口点。
void* _ArgList,// 传递给线程函数的参数,可以为 NULL。
unsigned _InitFlag,//线程创建标志,通常为 0。
unsigned* _ThrdAddr//用于接收线程 ID 的变量的指针,可以为 NULL。
);
下面是使用_beginthreadex和_endthreadex的一个简单示例。
#include <iostream>
#include <Windows.h>
#include <process.h>
// 线程函数
unsigned __stdcall ThreadFunction(void* lpParam)
{
while (true)
{
std::cout << "Hello, World!" << std::endl;
Sleep(1000); // 休眠1秒
_endthreadex(0); // 结束线程
}
return 0;
}
int main()
{
// 创建线程
uintptr_t hThread = _beginthreadex(
NULL, // 默认安全属性
0, // 默认堆栈大小
ThreadFunction, // 线程函数
NULL, // 线程函数参数
0, // 默认创建标志
NULL); // 不需要线程ID
// 防止主线程立即退出
WaitForSingleObject((HANDLE)hThread, INFINITE);
// 关闭线程句柄
CloseHandle((HANDLE)hThread);
return 0;
}
获取线程的句柄
通过_beginthreadex或者 CreateThread函数创建线程成功之后,可以获取到新创建线程的句柄。除此之外,还可以通过GetCurrentThread函数来获取当前正在运行的线程句柄。
HANDLE hThreadHandle = GetCurrentThread();
也可以调用GetCurrentThreadId获取正在运行的线程ID。
DWORD id = GetCurrentThreadId();
暂停线程和重新运行线程
调用SuspendThread函数和ResumeThread函数可以暂停线程或者重新运行线程。同时在创建的时候,可以传递线程创建标志CREATE_SUSPENDED,也可以使线程不用立即运行。
SuspendThread(hThreadHandle);
ResumeThread(hThreadHandle);
线程睡眠
通过调用Sleep函数可以使线程睡眠,操作系统将不会给当前线程分配CPU时间。注意Sleep函数的参数单位是毫秒。注意:如果给参数传递0,表示让操作系统调用另一个线程,强迫操作系统进行一次线程上下文切换,执行其他线程代码。
Sleep(1000);
获取线程上下文
每个线程都有自己的一个上下文,上下文记录和线程上一次执行的状态,包括寄存器状态、指令指针、堆栈信息、函数返回地址等等。我们可以通过GetThreadContext函数来获取线程的上下文信息,当然一般情况下,这个上下文信息,应用程序很少会去关注。
BOOL ret = GetThreadContext(hThreadHandle, px);
线程优先级
Windows操作系统会给每个进程分配一个0-31的线程优先级代码,应用程序不必手动设置这个优先级代码。操作系统在给每个线程分配CPU时间的时候,会依据不同的优先级号,从低到高给不同的线程分配不同的CPU时间。如果没有特殊需要,正常情况下,我们创建的多线程使用默认的线程优先级即可。
Windows提供了六个优先级的类:空闲、低于正常、正常、高于正常、高和实时。其中,正常就是默认情况下的进程优先级。基本上99%的进程都应该使用这个优先级。
程序可以通过SetThreadPriority来设置进程的优先级,原型如下:
BOOL SetThreadPriority(
HANDLE hThread,
int nPriority
);
其中第一个参数hThread表示线程句柄,第二个参数nPriority表示要调整的优先级,有7个选项。分别是
(1)、THREAD_PRIORITY_ABOVE_NORMAL :高于正常
(2)、THREAD_PRIORITY_BELOW_NORMAL: 低于正常
(3)、THREAD_PRIORITY_HIGHEST:最高
(4)、THREAD_PRIORITY_IDLE:空闲
(5)、THREAD_PRIORITY_LOWEST:最低
(6)、THREAD_PRIORITY_NORMAL:正常
(7)、THREAD_PRIORITY_TIME_CRITICAL:实时
Windows编程----线程管理的更多相关文章
- 从零开始山寨Caffe·叁:全局线程管理器
你需要一个管家,随手召唤的那种,想吃啥就吃啥. ——设计一个全局线程管理器 一个机器学习系统,需要管理一些公共的配置信息,如何存储这些配置信息,是一个难题. 设计模式 MVC框架 在传统的MVC编程框 ...
- 操作系统,windows编程,网络,socket
首发:个人博客,更新&纠错&回复 之前关于c/s的一篇博文只记了思路没记代码,而且表达不清晰,事后看不知所云,这个习惯要改. 这十几天学了点关于操作系统.windows编程和网络,主要 ...
- 有一定基础的 C++ 学习者该怎样学习 Windows 编程?
人的心理有个奇异的特性:一项知识一旦学会之后,学习过程中面临的困惑和不解非常快就会忘得干干净净,似乎一切都是自然而然,本来就该这种.因此,关于「怎样入门」这类问题,找顶尖高手来回答,未必能比一个刚入门 ...
- windows编程 进程的创建销毁和分析
Windows程序设计:进程 进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动,在Windows编程环境下,主要由两大元素组成: • 一个是操作系统用来管理进程的内核对象.操作系统使用内 ...
- windows编程经典书籍
本人是刚刚开始学习windows编程的,感觉看雪学院的大牛很NB.想找一些书籍来看学习学习,可是不知道看哪些书好.驱动,对菜鸟们来说真是一个很深奥的话题,所以 ,我找来了这篇文章供大家分享,以后大家发 ...
- windows编程之内核对象
学好windows编程,理解内核对象还是至关重要的(●'◡'●).闲话不多说,下面先来了解一下关于内核对象的知识: 内核对象(kernel object):内核对象是用于管理进 ...
- windows编程入门最重要的
要入门 Windows 编程,最重要的不是阅读什么教材,使用什么工具,而是先必须把以下几个对于初学者来说非常容易困惑的重要概念搞清楚: 1. 文字的编码和字符集.这部分需要掌握 ANSI 模式和 Un ...
- 我为什么学习Windows编程
前一段时间在看TCP/IP,在图书馆里面找了不少的书,其中有几本书还是不错的.比如: <Windows网络与通信程序设计(第二版)> 王艳平著 <WinSock网络编程经络> ...
- Windows编程 Windows程序的生与死(中)
<pre style=""><pre class="cpp" name="code">1 #include < ...
- C++ 11 多线程--线程管理
说到多线程编程,那么就不得不提并行和并发,多线程是实现并发(并行)的一种手段.并行是指两个或多个独立的操作同时进行.注意这里是同时进行,区别于并发,在一个时间段内执行多个操作.在单核时代,多个线程是并 ...
随机推荐
- 龙哥量化:期货交易软件:文华、博易大师闪电手、快期,同花顺期货通,金字塔,MC,MT5,TB交易开拓者横向对比分析
如果您需要代写公式, 请联系我. 龙哥QQ:591438821 龙哥微信:Long622889 此文档做对比分析, 我有空后给详细分析
- 关于QtCreator中三种不同编译版本 debug、release、profile 的区别
debug调试模式,编译后的可执行文件很大,带了很多调试符号信息等,方便开发阶段调试的时候进入具体的堆栈查看值.会打开所有的断言,运行阶段性能差速度慢,可能会有卡顿感觉. release发布模式,编译 ...
- 即时通讯框架MobileIMSDK的H5端开发快速入门
► 相关链接: ① MobileIMSDK-H5端的详细介绍 ② MobileIMSDK-H5端的开发手册new(* 精编PDF版) 一.技术准备 您是否已对Web端即时通讯技术有所了解? 1)新手入 ...
- 基于开源IM即时通讯框架MobileIMSDK:RainbowChat-iOS端v7.0版已发布
关于MobileIMSDK MobileIMSDK 是一套专门为移动端开发的开源IM即时通讯框架,超轻量级.高度提炼,一套API优雅支持 UDP .TCP .WebSocket 三种协议,支持 iOS ...
- elasticsearch数据导出和导入
数据导入和导出依赖于命令 elasticdump 数据导出 #!/bin/bash ES=http://ip:port ED=数据保存位置 datename=$(date +%Y-%m-%d) #da ...
- windows11使用pycharm连接wsl2开发基于poetry的python项目
windows11使用pycharm连接wsl2开发基于poetry的python项目 背景:公司开发的python项目用到了某个只提供了Linux版本的包,遂研究了一番如何在windows环境下进行 ...
- JavaScript 数组展平方法: flat() 和 flatMap()
从 ES2019 中开始引入了一种扁平化数组的新方法,可以展平任何深度的数组. flat flat() 方法创建一个新数组,其中所有子数组元素以递归方式连接到特定深度. 语法:array.flat(d ...
- HTTP协议超级详解【转载】
HTTP协议简介 超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式.协作式和超媒体信息系统的应用层协议.HTTP是万维网的数据通信的基础. ...
- Asp.net Core Kestrel 免费实现https
0.概述 先了解下https是个啥: https://www.bilibili.com/video/BV1j7411H7vV so!只要给我们的web服务器配置一个证书就行了,证书可以买,也可以用免费 ...
- C# 文件分割和文件合并
C# 文件分割和文件合并 void SplitFile() { string sourceFile = "Old.mp4"; // 源文件路径 string outputFile1 ...