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

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

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

创建线程

当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. Verilog5_有限状态机

    一.有限状态机(Finite State Machine, FSM)基本概念 有限状态机是由寄存器组和组合逻辑构成的硬件时序电路:         其状态只能在同一时钟跳变沿从一个状态转向另一个状态: ...

  2. 解决mapper重名问题

    问题 公司有一个集成开发平台,导入数据库表会自动生成实体类.mapper和xml等文件,这是一件很方便的事,可以省去很多没有技术性的重复工作. 但是最近我在使用这个平台的时候遇到了一个问题,那就是ma ...

  3. [LC1260]二维网格迁移

    二维网格迁移 题目描述 给你一个 m 行 n 列的二维网格 grid 和一个整数 k.你需要将 grid 迁移 k 次. 每次「迁移」操作将会引发下述活动: 位于 grid[i][j] 的元素将会移动 ...

  4. C Primer Plus 第6版 第二章 编程练习参考答案

    编译环境VS Code+WSL GCC /*第一题*************************/ #include<stdio.h> int main() { printf(&quo ...

  5. Index - 此处的诗

    虚构往事 正篇   嗯--本来发过两篇,但深愧于仓促的处理和并未完善的细节设定,隐藏了.   大概会是一个中篇的科幻故事,世界设定已经完善了(Shaya 可以作证!),但近期可能没有精力动笔. 番外 ...

  6. 如何高效发布Android AAR包到远程Maven仓库

    本文同步发布于公众号:移动开发那些事如何高效发布Android AAR包到远程Maven仓库 1 背景 在Gradle 7.0之前的版本中,maven插件是发布AAR包到远程Maven仓库的主要工具. ...

  7. C#正则表达式匹配候选词

    来自文心一言(多次修改才正确的): public App() { string input = "例子文字{备选,:'词1t324|备选词2gdfg,该方法|备选词3dsfdsf}继续{备选 ...

  8. Java之线程本地变量ThreadLocal-copy

    基本概念和用法 线程本地变量是说,每个线程都有同一个变量的独有拷贝,这个概念听上去比较难以理解,我们先直接来看类TheadLocal的用法. ThreadLocal是一个泛型类,接受一个类型参数T,它 ...

  9. 基于Redis组件的特性,实现一个分布式限流

    分布式---基于Redis进行接口IP限流 场景 为了防止我们的接口被人恶意访问,比如有人通过JMeter工具频繁访问我们的接口,导致接口响应变慢甚至崩溃,所以我们需要对一些特定的接口进行IP限流,即 ...

  10. labuladong的二分法查找模板

    几条规则: 1. while(left <= right)作为循环进入条件,退出则为left > right 循环内不再有return条件 2. nums[mid] == target之后 ...