系统中,进程主要有两部分组成:进程内核对象和进程地址空间。操作系统通过进程内核对象来管理进程,进程地址空间用于维护进程所需的资源:如代码、全局变量、资源文件等。

那么线程也是有两部分组成:线程内核对象和线程堆栈。操作系统通过线程内核对象对线程进行管理,线程堆栈用于维护线程执行代码时需要的所有的函数参数和局部变量。

线程的开销远小于进程,所以在并发执行多个任务的时候,应该尽可能使用多线程 解决问题,而不是使用多进程解决问题。

创建线程

当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编程----线程管理的更多相关文章

  1. 从零开始山寨Caffe·叁:全局线程管理器

    你需要一个管家,随手召唤的那种,想吃啥就吃啥. ——设计一个全局线程管理器 一个机器学习系统,需要管理一些公共的配置信息,如何存储这些配置信息,是一个难题. 设计模式 MVC框架 在传统的MVC编程框 ...

  2. 操作系统,windows编程,网络,socket

    首发:个人博客,更新&纠错&回复 之前关于c/s的一篇博文只记了思路没记代码,而且表达不清晰,事后看不知所云,这个习惯要改. 这十几天学了点关于操作系统.windows编程和网络,主要 ...

  3. 有一定基础的 C++ 学习者该怎样学习 Windows 编程?

    人的心理有个奇异的特性:一项知识一旦学会之后,学习过程中面临的困惑和不解非常快就会忘得干干净净,似乎一切都是自然而然,本来就该这种.因此,关于「怎样入门」这类问题,找顶尖高手来回答,未必能比一个刚入门 ...

  4. windows编程 进程的创建销毁和分析

    Windows程序设计:进程 进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动,在Windows编程环境下,主要由两大元素组成: • 一个是操作系统用来管理进程的内核对象.操作系统使用内 ...

  5. windows编程经典书籍

    本人是刚刚开始学习windows编程的,感觉看雪学院的大牛很NB.想找一些书籍来看学习学习,可是不知道看哪些书好.驱动,对菜鸟们来说真是一个很深奥的话题,所以 ,我找来了这篇文章供大家分享,以后大家发 ...

  6. windows编程之内核对象

          学好windows编程,理解内核对象还是至关重要的(●'◡'●).闲话不多说,下面先来了解一下关于内核对象的知识:       内核对象(kernel object):内核对象是用于管理进 ...

  7. windows编程入门最重要的

    要入门 Windows 编程,最重要的不是阅读什么教材,使用什么工具,而是先必须把以下几个对于初学者来说非常容易困惑的重要概念搞清楚: 1. 文字的编码和字符集.这部分需要掌握 ANSI 模式和 Un ...

  8. 我为什么学习Windows编程

    前一段时间在看TCP/IP,在图书馆里面找了不少的书,其中有几本书还是不错的.比如: <Windows网络与通信程序设计(第二版)> 王艳平著 <WinSock网络编程经络> ...

  9. Windows编程 Windows程序的生与死(中)

    <pre style=""><pre class="cpp" name="code">1 #include < ...

  10. C++ 11 多线程--线程管理

    说到多线程编程,那么就不得不提并行和并发,多线程是实现并发(并行)的一种手段.并行是指两个或多个独立的操作同时进行.注意这里是同时进行,区别于并发,在一个时间段内执行多个操作.在单核时代,多个线程是并 ...

随机推荐

  1. 3款.NET开源、功能强大的通讯调试工具,效率提升利器!

    前言 今天大姚给大家分享3款.NET开源.功能强大的通讯调试工具,帮助大家提高通讯调试的效率和准确性. LLCOM LLCOM是一个.NET开源的.功能强大的串口调试工具.支持Lua自动化处理.串口调 ...

  2. Qt音视频开发01-共享解码线程(耗时一年/性能凶残/至臻完美)

    一.前言 大概在8年前就开始用ffmpeg做视频解码的显示,第一个版本就100行代码左右,功能极其简单,就是开个线程解码视频流转成图片发给主界面绘制.时间过得真快,从当初的一胎到现在二胎都上学了三胎计 ...

  3. Qt数据库应用13-通用数据库分页

    一.前言 数据库分页展示,在所有的涉及到数据库记录的项目中都是需要的,除了简单的设备信息表.用户信息表这种很少几条几十条数据量的表除外,其余的日志记录表等都需要分页展示数据,少量的数据可以滚动条下拉查 ...

  4. UML之属性与参数的多重性

    在UML中,多重性是指一个条目潜在的数量范围.多重性可被用于属性.操作参数.关联关系.UML元模型也使用多重性对元模型元素之间的关系进行约束.多重性总是包含基数值,它是相关条目在现实世界中的确切数量. ...

  5. error: undefined reference to `cv::imread(cv::String const&, int)' 解决方法

    方法1 原文链接:https://blog.csdn.net/WhiteLiu/article/details/72901520 编译时出现下列错误: undefined reference to ' ...

  6. 即时通讯技术文集(第11期):IM通信格式的选型及Protobuf专题 [共16篇]

    为了更好地分类阅读52im.net 总计1000多篇精编文章,我将在每周三推送新的一期技术文集,本次是第11 期. [- 1 -] 如何选择即时通讯应用的数据传输格式 [链接] http://www. ...

  7. Solution -「ZJOI 2010」「洛谷 P2570」贪吃的老鼠

    \(\mathscr{Description}\)   Link.   有 \(n\) 块奶酪,每块奶酪出现时段为 \([s_i,t_i]\),体积为 \(V_i\):有 \(m\) 只老鼠要吃奶酪, ...

  8. Angular-教程

    https://www.runoob.com/angularjs/angularjs-tutorial.html https://www.runoob.com/angularjs2/angularjs ...

  9. MySQL---锁、变量、存储过程、游标、自定义函数

    一概述 数据库锁定机制简单来说,就是数据库为了保证数据的一致性,而使各种共享资源在被并发访问变得有序所设计的一种规则.对于任何一种数据库来说都需要有相应的锁定机制. MySQL各存储引擎使用了三种类型 ...

  10. 反射:获取Class 类的实例(四种方法)

    Class 类  对象照镜子后可以得到的信息:某个类的属性.方法和构造器.某个类到底实现了哪些接口.对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象.一个 Class 对象包含了 ...