函数
InterlockedIncrement
- 概述
- 这两个函数是
Windows
平台上进行线程安全原子操作的核心工具,用于实现高效的无锁多线程编程
- 这两个函数是
- 特性
- 原子性:
CPU
硬件保证操作的不可分割性 - 无锁:无需传统互斥锁(
mutex
),性能更高 - 内存屏障:隐含完整的读-写内存屏障语义
- 返回值:返回操作后的新值
- 原子性:
1 2 3 4 5 6 7 |
LONG InterlockedIncrement( volatile LONG* lpAddend // 指向要修改的 LONG 型变量 ); LONG InterlockedDecrement( volatile LONG* lpAddend // 指向要修改的 LONG 型变量 ); |
1 2 3 4 5 6 7 |
LONG64 InterlockedIncrement64( volatile LONG64* lpAddend ); LONG64 InterlockedDecrement64( volatile LONG64* lpAddend ); |
InterlockedExchange
-
概述
- 完全原子操作 的核心
Windows API
,专为多线程环境下的变量同步设计 - 通过
CPU
指令XCHG
(Exchange
)实现 - 执行期间锁定内存总线,阻止其他核心访问目标内存
- 完全原子操作 的核心
-
与锁(
Mutex
)的区别:
特性 | InterlockedExchange |
互斥锁(Mutex ) |
阻塞其他线程 | ❌ (单指令完成) | ✔️ (等待锁释放) |
开销 | ~10 ns |
~100 ns (上下文切换) |
适用场景 | 简单变量操作 | 复杂代码段保护 |
-
特性
- 线程安全:修改
LONG
(32
位) 时绝对原子 - 返回原始值:返回操作前的变量值(非写入值!)
- 线程安全:修改
-
示例1:
g_lCheckThradRuning
-
类型应为
LONG
(32
位有符号整型) -
通常声明为全局变量:
volatile LONG g_lCheckThradRuning = 0;
-
1 |
int lGenState = InterlockedExchange(&g_lCheckThradRuning, 1); |
- 示例2:开关标志位控制
1 2 3 4 5 6 7 |
// 多线程安全地启用/禁用功能 volatile LONG isEnabled = 0; void ToggleFeature() { LONG prev = InterlockedExchange(&isEnabled, isEnabled ? 0 : 1); // prev 可用于判断状态是否变化 } |
- 示例3:轻量级自旋锁
1 2 3 4 5 6 7 8 9 |
volatile LONG lockFlag = 0; // 0=未锁定, 1=锁定 void CriticalSection() { while (InterlockedExchange(&lockFlag, 1) == 1) { // 忙等待直到获得锁 } /* 临界区代码 */ InterlockedExchange(&lockFlag, 0); // 释放锁 } |
- 示例4:内存屏障作用
- 隐式包含 编译器屏障:阻止指令重排
- 强制内存操作顺序:
1 2 3 4 5 6 7 8 9 10 11 |
int data = 0; volatile LONG flag = 0; // 线程A data = 42; InterlockedExchange(&flag, 1); // 保证 data=42 在 flag=1 前完成 // 线程B if (InterlockedExchange(&flag, 1) == 1) { // 此处看到的 data 一定为 42 } |
VirtualAlloc
- 参数
- 直接操作虚拟内存管理器
1 2 3 4 5 6 |
LPVOID VirtualAlloc( LPVOID lpAddress, // 期望起始地址 (通常设 NULL 由系统决定) SIZE_T dwSize, // 分配大小 (按页对齐) DWORD flAllocationType, // 分配类型 (MEM_RESERVE | MEM_COMMIT) DWORD flProtect // 访问权限 (PAGE_READWRITE 等) ); |
- 特性
- 页粒度分配:默认
4KB
(x86/x64
) - 零开销控制:无堆管理元数据
- 页粒度分配:默认
- 内存保留和提交
MEM_RESERVE
:预留虚拟地址范围(无物理存储)MEM_COMMIT
:分配物理页/页面文件空间
1 2 3 4 5 |
// 预留 10MB 地址空间 LPVOID p = VirtualAlloc(NULL, 10 * 1024 * 1024, MEM_RESERVE, PAGE_READWRITE); // 实际提交第一页 (4KB) VirtualAlloc(p, 4096, MEM_COMMIT, PAGE_READWRITE); |
- 内存保护控制
权限类型 | 用途 |
PAGE_READONLY |
防意外写入的安全内存区 |
PAGE_EXECUTE_READ |
可执行代码区(动态代码生成) |
PAGE_GUARD |
创建警戒页(堆栈自动扩展) |
PAGE_NOACCESS |
故意触发访问异常的防护区域 |
- 高级操作
1 2 3 4 5 6 7 8 |
// 分配物理连续内存(驱动开发) MEM_EXTENDED_PARAMETER param = { .Type = MemExtendedParameterAttributeFlags }; param.Param = MEM_EXTENDED_PARAMETER_NONPAGED; VirtualAlloc2(NULL, &size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE, ¶m); // 大页分配(提升TLB效率) DWORD hugePage = 2 * 1024 * 1024; // 2MB VirtualAlloc(NULL, hugePage, MEM_RESERVE|MEM_COMMIT|MEM_LARGE_PAGES, PAGE_READWRITE); |
- 场景
- 超大内存分配
- 自定义内存池
- 安全敏感操作
- 内存映射文件基址
1 2 |
// 分配1GB内存 (避免CRT堆限制) auto bigMem = VirtualAlloc(0, 1LL<<30, MEM_COMMIT, PAGE_READWRITE); |
1 2 3 4 5 6 7 8 9 10 11 |
class MemoryPool { public: void* Alloc(size_t size) { // 在预分配块中切割... } MemoryPool() { poolBase_ = VirtualAlloc(0, POOL_SIZE, MEM_RESERVE); // 按需提交... } }; |
1 2 3 4 5 |
// 分配不可执行内存 (AntiROP) VirtualAlloc(0, size, MEM_COMMIT, PAGE_READONLY); // 敏感数据加密后存放 auto secret = VirtualAlloc(0, 4096, MEM_COMMIT, PAGE_READWRITE); |
1 2 3 |
// 精确控制文件映射位置 VirtualAlloc((void*)0x40000000, fileSize, MEM_RESERVE, ...); MapViewOfFileEx(hFile, 0x40000000, ...); |
VirtualAlloc
对比malloc
特性 | VirtualAlloc |
malloc |
操作系统层级 | Windows 内核级 API |
C/C++ 运行库函数 |
内存来源 | 虚拟内存管理器 (直接操作页表) | 进程堆管理器 (CRT 堆) |
分配单位 | 内存页 (4KB/2MB/1GB ) |
字节 (最小对齐 16 字节) |
分配开销 | 约 200ns (直接系统调用) |
50-100ns (用户态管理器) |
管理粒度 | 开发者完全控制 | CRT 自动管理 |
内存扩展 | 需显式提交/释放 | 自动增长堆空间 |
地址对齐 | 按页对齐 | 8/16 字节对齐 (x86/x64 ) |
元数据开销 | 0 字节 |
16-32 字节/块 (跟踪信息) |
大内存支持 | 支持 TB 级分配 |
最大通常为 1-2GB (调试堆限制) |
内存类型 | 可分配物理内存/页面文件/大页 | 仅用户态普通内存 |
错误处理 | GetLastError() 详细代码 |
返回 NULL (有限错误信息) |
QueryPerformanceCounter
- 介绍
- 获取当前高性能计时器的计数值
- 数值会随着时间线性增加
- 返回值
TRUE
:成功获取计数值FALSE
:失败(与QueryPerformanceFrequency
一致)
- 特点
- 单调递增:值只增不减,不受系统时间调整影响
- 极高分辨率:现代系统提供
100
纳秒级精度 CPU
时钟源:通常基于CPU
的TSC (Time Stamp Counter)
- 示例
1 2 3 4 5 6 |
UINT64 PerfTimer::GetTime() { LARGE_INTEGER li; QueryPerformanceCounter(&li); return li.QuadPart; } |
QueryPerformanceFrequency
- 介绍
- 获取高性能计时器的频率(分辨率)
- 返回计时器的 计数值/秒(赫兹
Hz
) - 只需调用一次(系统启动后频率固定)
- 返回值
TRUE
:系统支持高性能计时器FALSE
:系统不支持(较老的486
或早期奔腾处理器)
- 示例1
1 2 3 4 5 6 7 |
static LARGE_INTEGER freq = []() { LARGE_INTEGER f; QueryPerformanceFrequency(&f); return f; }(); auto diff_sec = perf_time / static_cast<double>(freq.QuadPart); |
- 示例2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <windows.h> #include <iostream> int main() { LARGE_INTEGER freq, start, end; // 1. 获取计时器频率(基础单位) QueryPerformanceFrequency(&freq); // 2. 记录开始时间 QueryPerformanceCounter(&start); // 3. 执行待测代码 Sleep(1000); // 模拟耗时操作 // 4. 记录结束时间 QueryPerformanceCounter(&end); // 5. 计算耗时(秒) double elapsedTime = static_cast<double>(end.QuadPart - start.QuadPart) / freq.QuadPart; std::cout << "执行耗时: " << elapsedTime << " 秒" << std::endl; return 0; } |
SetThreadPriority
- 概述
- 更改指定线程在系统调度器中的相对优先级,影响其获取
CPU
时间片的概率和频率
- 更改指定线程在系统调度器中的相对优先级,影响其获取
1 2 3 4 |
BOOL SetThreadPriority( HANDLE hThread, // 目标线程句柄 (GetCurrentThread()获取当前线程) int nPriority // 要设置的优先级值 (详见下方详解) ); |
nPriority
优先级常量 | 值 | 作用描述 | 典型应用场景 | ⚠️ 注意事项 |
THREAD_PRIORITY_IDLE |
-15 | 最低优先级:线程只在系统完全空闲时才运行(1% 以下CPU 负载)。 |
- 后台监控线程 - 系统级监视器 - 极低优先级日志 | 线程可能导致饥饿 |
THREAD_PRIORITY_LOWEST |
-2 | 最低可调度级:优先级大幅降低,但比IDLE 更容易获得CPU 时间。 |
- 文件后台下载 - 资源消耗型计算(不影响UI ) |
|
THREAD_PRIORITY_BELOW_NORMAL |
-1 | 略低于正常:稍低于进程默认优先级。 | - 辅助处理线程 - 次要数据同步 | |
THREAD_PRIORITY_NORMAL |
0 | 默认级别:继承进程优先级(通常是NORMAL_PRIORITY_CLASS )。 |
- 大多数常规线程 - UI 线程(需更高优先级) |
默认值 |
THREAD_PRIORITY_ABOVE_NORMAL |
+1 | 略高于正常:比进程优先级略高。 | - 重要后台任务 - 即时响应型计算(非实时) | |
THREAD_PRIORITY_HIGHEST |
+2 | 最高可调度级:显著提升优先级(在进程优先级范围内)。 | - 关键数据采集线程 - 音频视频处理核心 - 网络实时响应 | |
THREAD_PRIORITY_TIME_CRITICAL |
15 | 时间关键级:设置线程到其优先级类别的最高绝对优先级。 | - 硬件中断处理 - 实时控制系统 - 核心系统服务线程 | ⚠️可能导致其他线程饥饿或系统不稳定 |
C++23
std::unique_resource
- 概述
- 用于实现类型安全、通用的资源管理
- 设计
1 2 3 4 5 |
template< class Resource, // 资源类型(指针/句柄等) class Deleter // 自定义清理函数 > class unique_resource; |
- 特点
- 资源+删除器绑定:将资源对象与清理操作封装为单一实体
- 零开销抽象:编译器优化后性能媲美手动管理
- 异常安全保证:资源泄漏不可能的最终防线
- 功能
1 2 3 4 5 6 7 8 |
// 基础构造(显式所有权) unique_resource(Resource r, Deleter d); // 示例:文件管理 auto file = std::unique_resource( fopen("data.log", "w"), [](FILE* fp){ if(fp) fclose(fp); } ); |
1 2 3 4 5 6 7 |
Resource get() const noexcept; // 获取原始资源 Resource operator*() const noexcept; // 解引用访问 Resource operator->() const noexcept; // 指针式访问 // 使用示例 fprintf(file.get(), "Time: %lld\n", time(nullptr)); file->flush(); // 若资源有成员函数 |
1 2 3 4 5 6 7 8 9 |
// 释放所有权(返回资源并停止管理) Resource release() noexcept; // 重置资源(先清理当前,再接管新资源) void reset(Resource new_res = nullptr) noexcept; // 示例 Socket sock = connect_server(); file.reset(sock); // 先关文件,再接管socket |
- 进阶技巧
1 2 3 4 5 6 7 8 |
// 静态删除器(零内存开销) struct FileDeleter { static void operator()(FILE* f) noexcept { if(f) fclose(f); } }; using UniqueFile = std::unique_resource<FILE*, FileDeleter>; |
1 2 3 4 5 6 7 8 9 10 11 |
// 删除器作为空基类 template<auto DeleteFn> struct StatelessDeleter { void operator()(auto res) const noexcept { DeleteFn(res); } }; auto winHandle = std::unique_resource<HANDLE, StatelessDeleter<&CloseHandle>>( CreateEvent(...) ); |
1 2 3 4 |
// 资源所有权转移 auto move_resource(unique_resource&& src) { return std::unique_resource(src.release(), src.get_deleter()); } |
- 异常处理机制
1 2 3 4 5 6 7 |
try { auto res = std::unique_resource(acquire(), cleaner); process(res.get()); // 可能抛出异常 // 异常发生时:自动调用cleaner! } catch (...) { // 资源已清理,无需处理 } |
其他
一种延迟执行方法
- 非线程安全设计(应避免跨线程使用)
- 见
std::unique_resource
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#ifndef __VOS_DEFER_H__ #define __VOS_DEFER_H__ //实现defer关键字功能(函数退出调用),同一函数内定义多个defer调用顺序为后进先出(栈释放顺序) //使用时需要注意作用域 //用法上需要用()限定表达式, //使用方法,lamda:defer([&](){delete p;}()); 普通成员函数:defer(a.func(b)); //注意:MALLOC宏分配内存之后得FREE不可使用defer,pclint会报错 #define DEFER_1(x, y) x##y #define DEFER_2(x, y) DEFER_1(x, y) #define DEFER_0(x) DEFER_2(x, __COUNTER__) #ifndef PCLINT #define defer(expr) auto DEFER_0(_defered_option) = deferer([&](){expr;}) #else #define defer(expr) #endif template <typename Function> struct st_DoDefer { Function f; st_DoDefer(st_DoDefer&&) = default; // C++14 st_DoDefer(Function f) : f(std::move(f)) {} // st_DoDefer(Function f) : f(f) {} ~st_DoDefer() { f(); } }; template <typename Function> st_DoDefer<Function> deferer(Function f) { return st_DoDefer<Function>(f); } #endif |
1 2 3 4 5 6 |
defer([&]() { if (NULL != g_hEventFinished) { CloseHandle(g_hEventFinished); } }()); |
优先级工作机制 (调度器视角)
- 概述
- 续上节
SetThreadPriority
- 续上节
- 基础优先级计算:
- 例:若进程优先级为
NORMAL_PRIORITY_CLASS
(基础值8
),线程设置THREAD_PRIORITY_HIGHEST
(+2
),则最终调度优先级= 10
.
- 例:若进程优先级为
1 |
线程优先级 = 进程基础优先级 + 线程相对优先级 |
Windows
优先级范围表 (0-31
):THREAD_PRIORITY_IDLE
:强制线程优先级到0
(系统空闲进程级),无论进程优先级多高THREAD_PRIORITY_TIME_CRITICAL
:强制线程优先级到31
(硬件中断级)
1 2 3 4 5 6 7 8 |
| 范围 | 描述 | |----------|--------------------------| | 0 | System Idle Process | | 1-6 | Low-priority applications| | 7-15 | Normal applications | | 16-22 | High-priority tasks | | 23-26 | Real-time critical | | 27-31 | Hardware interrupts | |
- 调度策略:
- 高优先级线程可抢占低优先级线程的
CPU
时间片 - 同级线程按时间片轮转调度(约
10-15ms/
次)
- 高优先级线程可抢占低优先级线程的
THREAD_PRIORITY_IDLE
深度分析
- 概述
- 续上节
SetThreadPriority
- 续上节
1 |
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE); |
- 具体行为
- 强制线程基础优先级为0(系统最低级)
- 调度策略:
其他所有优先级 > 0 的线程运行完毕后才考虑该线程
即使进程优先级设为HIGH_PRIORITY_CLASS
(13)也无效
- 场景
1 2 3 4 5 6 7 |
// 场景:后台心跳检测线程(绝对不影响业务) while (true) { if (IsSystemIdle()) { // 仅在系统空闲时运行逻辑 CheckServiceHealth(); } Sleep(60000); // 每分钟唤醒一次 } |
- 风险
1 2 3 4 5 6 7 8 |
// 错误用法:在关键线程上设置IDLE std::thread worker([]() { SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE); // ❌灾难! while (true) { ProcessRealTimeData(); // 此代码可能永不执行 } }); worker.detach(); |
THREAD_PRIORITY_TIME_CRITICAL
注意事项
- 概述
- 续上节
SetThreadPriority
- 续上节
- 使用原则
1 2 3 4 5 6 |
// 正确:只在极短任务中使用高优先级 void InterruptHandler() { SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); ReadHardwareRegister(); // 硬件寄存器读取(<1ms操作) SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); // ✅及时恢复 } |
线程优先级实战选择建议
需求场景 | 推荐优先级 | 代码示例 |
后台日志写入 | THREAD_PRIORITY_IDLE |
磁盘I/O密集 + 允许延迟 |
用户界面渲染 | THREAD_PRIORITY_ABOVE_NORMAL |
保证UI流畅响应 (优先级 > 计算线程) |
网络数据包处理 | THREAD_PRIORITY_HIGHEST |
避免数据堆积,确保实时性 |
硬件驱动中断服务 | THREAD_PRIORITY_TIME_CRITICAL |
要求μs级响应 (需严格控制执行时间) |
常规数据处理 | THREAD_PRIORITY_NORMAL |
默认策略,无特殊要求时使用 |
线程优先级与I/O
关系
- 线程优先级不直接影响磁盘或网络I/O优先级
- 需额外设置:
1 2 3 4 5 |
// 设置线程I/O优先级为最低(配合THREAD_PRIORITY_IDLE) SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE); SetFileIoOverlappedRange(hFile, 0); // Windows内部API(不公开) // 或使用标准方式: SetPriorityClass(GetCurrentProcess(), PROCESS_MODE_BACKGROUND_BEGIN); |
声明:本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ Windbg:命令实践详解一03/27
- ♥ 打包_7z生成自解压打包exe07/11
- ♥ 关于异常的捕获和dump文件的生成07/05
- ♥ Windbg关于死锁的简单调试分析总结09/13
- ♥ Dump分析:空指针访问二,重复释放堆内存二03/30
- ♥ WinDbg命令标记、命令07/11