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 多线程--线程管理
说到多线程编程,那么就不得不提并行和并发,多线程是实现并发(并行)的一种手段.并行是指两个或多个独立的操作同时进行.注意这里是同时进行,区别于并发,在一个时间段内执行多个操作.在单核时代,多个线程是并 ...
随机推荐
- Linux系统手动安装Firefox浏览器
大多数Linux发行版都以Firefox作为默认的浏览器,并可以轻松地从软件库中安装.例如:Debian/Ubuntu: sudo apt-get install firefoxFedora: sud ...
- Qt音视频开发37-识别鼠标按下像素坐标
一.前言 在和视频交互过程中,用户一般需要在显示视频的通道上点击对应的区域,弹出对应的操作按钮,将当前点击的区域或者绘制的多边形区域坐标或者坐标点集合,发送出去,通知其他设备进行处理.比如识别到很多人 ...
- Qt音视频开发4-vlc读取和控制
一.前言 vlc本身是个全功能的很牛逼的播放器,你能够想到的播放的功能他都有,比如获取视频文件的长度.唱片的封面.当前播放进度.设置播放进度.声音控制.静音控制等,这些vlc都给你封装好了,你直接调用 ...
- 报错test_features2d.cpp:51:10: fatal error: features2d/test/test_detectors_regression.impl.hpp: 没有那个文件
问题描述: ubuntu18.04安装opencv4.5.1+contrib 报错test_features2d.cpp:51:10: fatal error: features2d/test/tes ...
- [转]使用navicat将excel文件导入mysql数据库
excel: 注: 1.mysql里建立一张跟excel一样的表结构的表(包含id) 2.excel最好没有任何格式,只是纯值,不然会出现导入不了的错误 ----------------------- ...
- Hugo 静态博客部署
I. 前提条件 1.1 安装 Hugo 1.1.1 Windows 1.下载 Hugo(建议下载扩展版):Hugo(github.com) 2.解压 Hugo 压缩包到指定目录. 3.[Win + R ...
- Yano 的 2024 观影总结
前言 2024 年马上就要过去了,总结下自己在 2024 年看过的影视作品. PS:我是用 Cursor 写的,所有有一些文字可能是 Cursor 自动补全的,不是我写的 0_o 电影 <怪物& ...
- 配置Ubuntu上的NFS
$sudo apt-get install nfs-kernel-server nfs-common 配置 $sudo vim /etc/exports#添加#/home/pi/project/roo ...
- Linux 压缩命令集合
01. tar格式 解包:tar xvf FileName.tar 打包:tar cvf FileName.tar DirName(注:tar是打包,不是压缩!) 02. gz格式 解压1:gunzi ...
- .NET Core 委托原理解析
.NET Core 委托原理解析 在 .NET Core 中,委托(Delegate)是一种类型安全的函数指针,它允许你将方法作为参数传递给其他方法,或者将方法存储在变量中以便稍后调用.委托在事件处理 ...