本文献给:
已掌握C语言基础,希望全面理解Windows环境下多线程编程的开发者。本文将深入讲解Windows线程的核心概念、高级特性、同步机制,并对相似概念进行辨析,帮助您构建一个稳健的并发程序。
你将学到:
- 线程与进程的对比及现代系统架构
- Windows线程的完整生命周期管理与控制
- 多种线程同步机制的实现
- 线程池、纤程等高级并发概念
- 性能优化、调试技巧与实践
**本文适用平台:**Windows 7/8/10/11,Visual Studio 2015+,支持C11/C17的编译器
目录
- 第一部分:线程与进程的理解
-
- [1. 现代操作系统中的执行单元](#1. 现代操作系统中的执行单元)
- [2. Windows线程架构详解](#2. Windows线程架构详解)
- 第二部分:Windows线程的创建与管理
-
- [1. 线程创建:传统与现代API对比](#1. 线程创建:传统与现代API对比)
- [2. 线程生命周期管理](#2. 线程生命周期管理)
- [3. 线程本地存储(TLS)](#3. 线程本地存储(TLS))
- [4. 线程优先级与调度](#4. 线程优先级与调度)
- 第三部分:Windows线程同步机制详解
-
- [1. 同步机制选择指南](#1. 同步机制选择指南)
- [2. 临界区(Critical Section)深入](#2. 临界区(Critical Section)深入)
- [3. 互斥量(Mutex)高级用法](#3. 互斥量(Mutex)高级用法)
- [4. 事件(Event)机制全面解析](#4. 事件(Event)机制全面解析)
- [5. 信号量(Semaphore)与资源池](#5. 信号量(Semaphore)与资源池)
- [6. 现代同步(Windows Vista+)](#6. 现代同步(Windows Vista+))
- [7. 原子操作与内存屏障](#7. 原子操作与内存屏障)
- 第四部分:高级线程概念
-
- [1. 线程池(Thread Pool)](#1. 线程池(Thread Pool))
- [2. 纤程(Fiber)](#2. 纤程(Fiber))
- [3. I/O完成端口(IOCP)](#3. I/O完成端口(IOCP))
- 第五部分:线程安全与最佳实践
-
- [1. 线程安全设计模式](#1. 线程安全设计模式)
- [2. 死锁预防与检测](#2. 死锁预防与检测)
- [3. 性能优化技巧](#3. 性能优化技巧)
- [4. 调试与诊断工具](#4. 调试与诊断工具)
- 第六部分:现代发展与未来趋势
-
- [1. C11/C17标准线程支持](#1. C11/C17标准线程支持)
- [2. 并行算法库](#2. 并行算法库)
- [3. GPU计算与异构编程](#3. GPU计算与异构编程)
- 第七部分:总结
-
- [1. 技术选型](#1. 技术选型)
- [2. 线程安全检查清单](#2. 线程安全检查清单)
- [3. 常见陷阱与解决方案](#3. 常见陷阱与解决方案)
第一部分:线程与进程的理解
1. 现代操作系统中的执行单元
在Windows操作系统中,进程 是资源分配的基本单位,而线程是CPU调度的基本单位。一个进程包含至少一个线程(主线程),并可以创建多个线程来并行执行任务。
核心概念辨析
| 概念 | 定义 | 资源拥有情况 | 创建开销 | 通信方式 |
|---|---|---|---|---|
| 进程 | 程序的执行实例,拥有独立地址空间 | 独立的内存、文件句柄、系统资源 | 大(需创建虚拟地址空间、加载程序映像) | 管道、共享内存、消息、套接字、RPC等 |
| 线程 | 进程内的执行流,共享进程资源 | 共享进程内存和资源,拥有独立栈、寄存器、线程本地存储 | 小(仅需分配栈和线程控制块) | 直接共享内存(需同步)、消息队列 |
现代系统架构演进
传统模型: 一个进程对应一个线程(单线程进程)
现代模型: 一个进程包含多个线程(多线程进程)
发展趋势:
- **异步编程模型:**I/O完成端口、异步I/O
- **用户态调度:**纤程(Fiber)、协程
- **硬件并发:**SIMD指令集、GPU计算
- **微内核设计:**更小的进程上下文切换开销
2. Windows线程架构详解
内核线程 vs 用户线程
- **内核线程:**由操作系统内核直接管理的线程,调度由内核完成
- **用户线程:**早期概念,现在Windows的线程都是内核线程
- Windows实现: 每个线程都有一个对应的ETHREAD内核对象
线程优先级体系
c
// Windows线程优先级常量
#define THREAD_PRIORITY_IDLE (-15)
#define THREAD_PRIORITY_LOWEST (-2)
#define THREAD_PRIORITY_BELOW_NORMAL (-1)
#define THREAD_PRIORITY_NORMAL 0
#define THREAD_PRIORITY_ABOVE_NORMAL 1
#define THREAD_PRIORITY_HIGHEST 2
#define THREAD_PRIORITY_TIME_CRITICAL 15
// 动态优先级提升:Windows会临时提升等待I/O的线程优先级
线程状态变迁
新建/Created
就绪/Ready
运行/Running
阻塞/Blocked
终止/Terminated
等待/Waiting
挂起/Suspended
第二部分:Windows线程的创建与管理
1. 线程创建:传统与现代API对比
传统API:CreateThread
c
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
// 线程函数签名:DWORD WINAPI ThreadProc(LPVOID lpParameter)
DWORD WINAPI WorkerThread(LPVOID lpParam) {
int threadId = *(int*)lpParam;
_tprintf(_T("线程 %d 开始执行,线程ID: %d\n"),
threadId, GetCurrentThreadId());
// 模拟工作
for (int i = 0; i < 5; i++) {
Sleep(1000);
_tprintf(_T("线程 %d 正在工作...\n"), threadId);
}
_tprintf(_T("线程 %d 执行完毕\n"), threadId);
return 0; // 返回值可通过GetExitCodeThread获取
}
int _tmain(int argc, _TCHAR* argv[]) {
HANDLE hThreads[3];
DWORD threadIds[3];
int threadParams[3] = {1, 2, 3};
for (int i = 0; i < 3; i++) {
hThreads[i] = CreateThread(
NULL, // 默认安全属性
0, // 默认栈大小(通常为1MB)
WorkerThread, // 线程函数
&threadParams[i], // 线程参数
0, // 创建标志:0-立即运行,CREATE_SUSPENDED-挂起
&threadIds[i] // 输出线程ID
);
if (hThreads[i] == NULL) {
_tprintf(_T("创建线程失败!错误码: %d\n"), GetLastError());
return 1;
}
}
// 等待所有线程完成
WaitForMultipleObjects(3, hThreads, TRUE, INFINITE);
// 关闭句柄
for (int i = 0; i < 3; i++) {
CloseHandle(hThreads[i]);
}
_tprintf(_T("所有线程执行完毕\n"));
return 0;
}
推荐API:_beginthreadex(CRT安全版本)
c
#include <process.h> // _beginthreadex需要此头文件
#include <windows.h>
unsigned int __stdcall CRTThread(void* param) {
int id = *(int*)param;
printf("CRT线程 %d 运行中\n", id);
// CRT函数(如printf, malloc)在线程中是安全的
return 0;
}
void CreateCRTThreads() {
HANDLE hThread;
unsigned threadID;
// _beginthreadex内部会初始化CRT的线程特定数据
hThread = (HANDLE)_beginthreadex(
NULL, // 安全属性
0, // 栈大小
CRTThread, // 线程函数
NULL, // 参数
0, // 初始状态:0-立即运行
&threadID // 线程ID
);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
}
API辨析:CreateThread vs _beginthreadex
| 特性 | CreateThread | _beginthreadex |
|---|---|---|
| 头文件 | windows.h | process.h |
| 线程函数签名 | DWORD WINAPI ThreadProc(LPVOID) | unsigned __stdcall ThreadProc(void*) |
| CRT初始化 | 不初始化线程特定数据 | 初始化CRT线程特定数据 |
| 内存泄漏风险 | 使用CRT函数可能导致内存泄漏 | 正确管理CRT资源 |
| 返回值获取 | 通过GetExitCodeThread | 直接返回退出代码 |
| 建议使用 | 纯Win32 API,不使用CRT | 使用CRT函数(printf, malloc等) |
2. 线程生命周期管理
线程挂起与恢复
c
// 创建时挂起
HANDLE hThread = CreateThread(NULL, 0, ThreadFunc, NULL, CREATE_SUSPENDED, NULL);
// 获取挂起计数
DWORD suspendCount = SuspendThread(hThread); // 增加挂起计数
suspendCount = ResumeThread(hThread); // 减少挂起计数
// 注意:ResumeThread返回之前的挂起计数
// 当返回值为1时,线程恢复运行;为0时,线程已在运行状态
线程终止的正确方式
c
// 方式1:自然退出(推荐)
DWORD WINAPI ThreadFunc(LPVOID param) {
// 正常执行逻辑
return 0; // 线程自然退出
}
// 方式2:ExitThread(线程内调用)
void ExitThreadExample() {
// 清理线程特定资源
// ...
ExitThread(0); // 立即退出线程,不执行函数返回
}
// 方式3:TerminateThread(外部强制终止,不推荐!)
void DangerousTermination(HANDLE hThread) {
// 危险!可能导致资源泄漏、状态不一致
TerminateThread(hThread, 0);
}
// 方式4:优雅终止请求
volatile BOOL g_shouldExit = FALSE;
DWORD WINAPI WorkerWithExitFlag(LPVOID param) {
while (!g_shouldExit) {
// 执行工作
Sleep(100);
}
return 0;
}
线程退出代码获取
c
HANDLE hThread = CreateThread(/* ... */);
DWORD exitCode;
// 方式1:线程结束后获取
WaitForSingleObject(hThread, INFINITE);
GetExitCodeThread(hThread, &exitCode);
_tprintf(_T("线程退出代码: %d\n"), exitCode);
// 方式2:轮询方式
while (true) {
if (GetExitCodeThread(hThread, &exitCode)) {
if (exitCode != STILL_ACTIVE) {
_tprintf(_T("线程已退出,代码: %d\n"), exitCode);
break;
}
}
Sleep(100); // 避免忙等待
}
3. 线程本地存储(TLS)
动态TLS
c
// 分配TLS索引(进程全局)
DWORD tlsIndex = TlsAlloc();
// 在线程中设置/获取TLS值
DWORD WINAPI ThreadFunc(LPVOID param) {
// 分配线程特定数据
LPVOID threadData = malloc(100);
TlsSetValue(tlsIndex, threadData);
// 获取线程特定数据
LPVOID data = TlsGetValue(tlsIndex);
// 使用数据...
// 清理
free(threadData);
return 0;
}
// 进程退出时释放TLS索引
TlsFree(tlsIndex);
静态TLS(编译器支持)
c
// 使用__declspec(thread)声明线程局部变量
__declspec(thread) int tls_var = 0;
DWORD WINAPI ThreadFunc(LPVOID param) {
tls_var = GetCurrentThreadId(); // 每个线程有自己的副本
printf("线程ID: %d, tls_var: %d\n", GetCurrentThreadId(), tls_var);
return 0;
}
4. 线程优先级与调度
设置线程优先级
c
HANDLE hThread = CreateThread(/* ... */);
// 设置相对优先级(基于进程优先级类)
BOOL success = SetThreadPriority(hThread, THREAD_PRIORITY_ABOVE_NORMAL);
// 获取当前优先级
int priority = GetThreadPriority(hThread);
// 设置绝对优先级(较少使用)
// 需要SE_TCB_NAME权限
success = SetThreadPriorityBoost(hThread, FALSE); // 禁用优先级提升
线程亲和性(Processor Affinity)
c
// 设置线程只能在指定CPU核心上运行
DWORD_PTR affinityMask = 0x01; // 只允许在CPU 0上运行
DWORD_PTR previousMask = SetThreadAffinityMask(hThread, affinityMask);
// 在多核系统中优化缓存性能
void SetOptimalAffinity() {
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
// 避免核心0(通常为系统核心)
DWORD_PTR mask = 0;
for (int i = 1; i < sysInfo.dwNumberOfProcessors; i++) {
mask |= (1 << i);
}
SetThreadAffinityMask(GetCurrentThread(), mask);
}
第三部分:Windows线程同步机制详解
1. 同步机制选择指南
同步原语对比矩阵
| 机制 | 作用范围 | 性能 | 适用场景 | 能否命名 | 线程/进程 | 等待超时 |
|---|---|---|---|---|---|---|
| 临界区 | 进程内 | ★★★★★ | 进程内多线程同步 | 否 | 线程 | 否 |
| 互斥量 | 跨进程 | ★★★☆☆ | 跨进程同步,所有权概念 | 能 | 两者 | 能 |
| 事件 | 跨进程 | ★★★★☆ | 线程间通知,条件等待 | 能 | 两者 | 能 |
| 信号量 | 跨进程 | ★★★☆☆ | 控制资源访问数量 | 能 | 两者 | 能 |
| 读写锁 | Windows SRW | ★★★★☆ | 读多写少场景 | 否 | 线程 | 否 |
| 条件变量 | Windows CV | ★★★★☆ | 复杂条件等待 | 否 | 线程 | 能 |
| 互锁操作 | 进程内 | ★★★★★ | 简单原子操作 | N/A | 线程 | N/A |
2. 临界区(Critical Section)深入
基础使用
c
#include <windows.h>
CRITICAL_SECTION g_cs;
int g_sharedData = 0;
void InitializeCritical() {
InitializeCriticalSection(&g_cs); // 初始化
// 高级初始化:自旋计数
// InitializeCriticalSectionAndSpinCount(&g_cs, 4000);
}
DWORD WINAPI ThreadFunc(LPVOID param) {
EnterCriticalSection(&g_cs); // 进入临界区
// 安全访问共享数据
g_sharedData++;
LeaveCriticalSection(&g_cs); // 离开临界区
return 0;
}
void CleanupCritical() {
DeleteCriticalSection(&g_cs); // 清理
}
尝试进入临界区
c
// 非阻塞方式尝试
if (TryEnterCriticalSection(&g_cs)) {
// 成功获取锁
// 操作共享资源...
LeaveCriticalSection(&g_cs);
} else {
// 锁被其他线程持有,执行其他操作
// ...
}
// 带超时的等待(需要条件变量配合,后面介绍)
临界区与自旋锁
c
// 初始化带自旋计数的临界区
CRITICAL_SECTION cs;
InitializeCriticalSectionAndSpinCount(&cs, 4000);
// 自旋锁概念辨析:
// 1. 临界区的自旋:在用户态忙等待一段时间,避免立即进入内核态
// 2. 真正的自旋锁(SpinLock):完全在用户态忙等待,适用于极短临界区
// 3. 内核自旋锁:驱动开发中使用
// 修改自旋计数
SetCriticalSectionSpinCount(&cs, 8000);
3. 互斥量(Mutex)高级用法
基础互斥量
c
HANDLE g_hMutex = NULL;
void MutexExample() {
// 创建互斥量
g_hMutex = CreateMutex(
NULL, // 默认安全属性
FALSE, // 初始拥有者:FALSE-无主,TRUE-创建线程拥有
NULL // 名称:NULL-未命名,或指定名称跨进程使用
);
// 命名互斥量(跨进程)
// HANDLE hNamedMutex = CreateMutex(NULL, FALSE, L"Global\\MyAppMutex");
DWORD waitResult;
// 等待互斥量
waitResult = WaitForSingleObject(g_hMutex, INFINITE);
if (waitResult == WAIT_OBJECT_0) {
// 成功获取互斥量
// 访问共享资源...
// 释放互斥量
ReleaseMutex(g_hMutex);
} else if (waitResult == WAIT_ABANDONED) {
// 互斥量被遗弃(前一个拥有者未释放就终止)
// 需要处理数据可能不一致的情况
// 但仍然可以获取互斥量,需重建状态
ReleaseMutex(g_hMutex);
}
// 清理
CloseHandle(g_hMutex);
}
互斥量所有权与遗弃状态
c
// 检测和处理遗弃的互斥量
HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);
DWORD WINAPI ProblemThread(LPVOID param) {
WaitForSingleObject(hMutex, INFINITE);
// 发生异常或直接退出,未释放互斥量!
// 线程终止,互斥量被标记为"遗弃"
return 0; // 实际上不会执行到这里
}
DWORD WINAPI RecoveryThread(LPVOID param) {
DWORD result = WaitForSingleObject(hMutex, INFINITE);
if (result == WAIT_ABANDONED) {
printf("警告:互斥量被遗弃!数据可能不一致。\n");
// 必须重建共享状态的一致性
// 然后正常使用互斥量...
}
ReleaseMutex(hMutex);
return 0;
}
4. 事件(Event)机制全面解析
事件类型与使用
c
// 创建手动重置事件
HANDLE hManualEvent = CreateEvent(
NULL, // 安全属性
TRUE, // 手动重置:TRUE-手动,FALSE-自动
FALSE, // 初始状态:FALSE-无信号
NULL // 名称
);
// 创建自动重置事件
HANDLE hAutoEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
// 事件操作
SetEvent(hManualEvent); // 设置为有信号状态
ResetEvent(hManualEvent); // 设置为无信号状态(仅手动重置事件有效)
PulseEvent(hManualEvent); // 设置信号后立即重置(已过时,不推荐使用)
// 高级事件操作
HANDLE hEvent = CreateEventEx(
NULL, // 安全属性
NULL, // 名称
CREATE_EVENT_MANUAL_RESET, // 标志
EVENT_ALL_ACCESS // 访问权限
);
生产者-消费者模式完整实现
c
#include <windows.h>
#include <queue>
// 共享队列和同步对象
std::queue<int> g_queue;
CRITICAL_SECTION g_queueCS;
HANDLE g_hQueueNotEmpty; // 自动重置事件
HANDLE g_hQueueNotFull; // 手动重置事件
const int MAX_QUEUE_SIZE = 10;
DWORD WINAPI Producer(LPVOID param) {
int producerId = *(int*)param;
for (int i = 0; i < 20; i++) {
// 等待队列不满
WaitForSingleObject(g_hQueueNotFull, INFINITE);
EnterCriticalSection(&g_queueCS);
// 生产数据
int data = producerId * 1000 + i;
g_queue.push(data);
printf("生产者 %d 生产: %d,队列大小: %d\n",
producerId, data, g_queue.size());
// 如果队列之前为空,通知消费者
if (g_queue.size() == 1) {
SetEvent(g_hQueueNotEmpty);
}
// 如果队列已满,重置"队列不满"事件
if (g_queue.size() == MAX_QUEUE_SIZE) {
ResetEvent(g_hQueueNotFull);
}
LeaveCriticalSection(&g_queueCS);
Sleep(rand() % 100); // 模拟生产时间
}
return 0;
}
DWORD WINAPI Consumer(LPVOID param) {
int consumerId = *(int*)param;
for (int i = 0; i < 10; i++) {
// 等待队列不空
WaitForSingleObject(g_hQueueNotEmpty, INFINITE);
EnterCriticalSection(&g_queueCS);
// 消费数据
int data = g_queue.front();
g_queue.pop();
printf("消费者 %d 消费: %d,队列大小: %d\n",
consumerId, data, g_queue.size());
// 如果队列之前为满,通知生产者
if (g_queue.size() == MAX_QUEUE_SIZE - 1) {
SetEvent(g_hQueueNotFull);
}
// 如果队列已空,重置"队列不空"事件
if (g_queue.size() == 0) {
ResetEvent(g_hQueueNotEmpty);
}
LeaveCriticalSection(&g_queueCS);
Sleep(rand() % 150); // 模拟消费时间
}
return 0;
}
5. 信号量(Semaphore)与资源池
信号量基础
c
// 创建信号量
HANDLE hSemaphore = CreateSemaphore(
NULL, // 安全属性
3, // 初始计数:允许3个线程同时访问
5, // 最大计数
NULL // 名称
);
DWORD WINAPI ResourceUser(LPVOID param) {
// 等待信号量(减少计数)
WaitForSingleObject(hSemaphore, INFINITE);
// 使用受保护的资源
printf("线程 %d 正在使用资源\n", GetCurrentThreadId());
Sleep(1000);
// 释放信号量(增加计数)
ReleaseSemaphore(hSemaphore, 1, NULL);
return 0;
}
连接池实现
c
class ConnectionPool {
private:
HANDLE m_hSemaphore;
CRITICAL_SECTION m_cs;
std::vector<HANDLE> m_connections;
std::vector<bool> m_inUse;
public:
ConnectionPool(int poolSize) {
InitializeCriticalSection(&m_cs);
m_hSemaphore = CreateSemaphore(NULL, poolSize, poolSize, NULL);
// 创建模拟连接
for (int i = 0; i < poolSize; i++) {
m_connections.push_back((HANDLE)(i + 1)); // 模拟连接句柄
m_inUse.push_back(false);
}
}
~ConnectionPool() {
DeleteCriticalSection(&m_cs);
CloseHandle(m_hSemaphore);
}
HANDLE AcquireConnection(DWORD timeout) {
// 等待可用连接
if (WaitForSingleObject(m_hSemaphore, timeout) != WAIT_OBJECT_0) {
return NULL; // 超时
}
EnterCriticalSection(&m_cs);
// 查找空闲连接
HANDLE connection = NULL;
for (size_t i = 0; i < m_connections.size(); i++) {
if (!m_inUse[i]) {
m_inUse[i] = true;
connection = m_connections[i];
break;
}
}
LeaveCriticalSection(&m_cs);
return connection;
}
void ReleaseConnection(HANDLE connection) {
EnterCriticalSection(&m_cs);
// 标记连接为空闲
for (size_t i = 0; i < m_connections.size(); i++) {
if (m_connections[i] == connection) {
m_inUse[i] = false;
break;
}
}
LeaveCriticalSection(&m_cs);
// 释放信号量
ReleaseSemaphore(m_hSemaphore, 1, NULL);
}
};
6. 现代同步(Windows Vista+)
读写锁(SRW Lock)
c
#include <windows.h>
SRWLOCK g_srwLock;
int g_readCount = 0;
int g_data = 0;
// 读线程(共享访问)
DWORD WINAPI Reader(LPVOID param) {
AcquireSRWLockShared(&g_srwLock); // 获取共享锁
// 读取数据
printf("读者 %d: 数据=%d, 读者数=%d\n",
GetCurrentThreadId(), g_data, ++g_readCount);
Sleep(100);
--g_readCount;
ReleaseSRWLockShared(&g_srwLock); // 释放共享锁
return 0;
}
// 写线程(独占访问)
DWORD WINAPI Writer(LPVOID param) {
AcquireSRWLockExclusive(&g_srwLock); // 获取独占锁
// 修改数据
g_data++;
printf("写者 %d: 写入数据=%d\n", GetCurrentThreadId(), g_data);
Sleep(200);
ReleaseSRWLockExclusive(&g_srwLock); // 释放独占锁
return 0;
}
// 尝试获取锁
BOOL TryAcquire() {
if (TryAcquireSRWLockShared(&g_srwLock)) {
// 成功获取共享锁
// ...
ReleaseSRWLockShared(&g_srwLock);
return TRUE;
}
return FALSE;
}
条件变量(Condition Variable)
c
// 结合临界区的条件变量
CRITICAL_SECTION g_cs;
CONDITION_VARIABLE g_cv;
BOOL g_ready = FALSE;
DWORD WINAPI Waiter(LPVOID param) {
EnterCriticalSection(&g_cs);
// 等待条件满足
while (!g_ready) {
// 进入等待,会自动释放临界区,被唤醒后重新获取
SleepConditionVariableCS(&g_cv, &g_cs, INFINITE);
}
// 条件满足,执行操作
printf("条件满足,继续执行\n");
LeaveCriticalSection(&g_cs);
return 0;
}
DWORD WINAPI Notifier(LPVOID param) {
Sleep(1000); // 模拟工作
EnterCriticalSection(&g_cs);
g_ready = TRUE;
// 通知一个等待线程
WakeConditionVariable(&g_cv);
// 或通知所有等待线程:WakeAllConditionVariable(&g_cv);
LeaveCriticalSection(&g_cs);
return 0;
}
// 使用SRW锁的条件变量
SRWLOCK g_srw;
CONDITION_VARIABLE g_cv_srw;
void WaitWithSRW() {
AcquireSRWLockShared(&g_srw);
// 等待条件
SleepConditionVariableSRW(&g_cv_srw, &g_srw, INFINITE, CONDITION_VARIABLE_LOCKMODE_SHARED);
ReleaseSRWLockShared(&g_srw);
}
一次性初始化(One-Time Initialization)
c
INIT_ONCE g_initOnce = INIT_ONCE_STATIC_INIT;
BOOL CALLBACK InitOnceCallback(
PINIT_ONCE InitOnce,
PVOID Parameter,
PVOID *Context
) {
// 这个回调只会执行一次
printf("一次性初始化被执行\n");
*Context = malloc(100); // 分配资源
return TRUE;
}
DWORD WINAPI ThreadFunc(LPVOID param) {
PVOID context;
// 每个线程都调用,但初始化只发生一次
InitOnceExecuteOnce(&g_initOnce, InitOnceCallback, NULL, &context);
// 使用初始化后的资源
// ...
return 0;
}
7. 原子操作与内存屏障
互锁函数(Interlocked Functions)
c
// 原子加法
LONG value = 0;
LONG result = InterlockedAdd(&value, 5); // value += 5,原子操作
// 原子比较交换(CAS)
LONG expected = 10;
LONG desired = 20;
LONG original = InterlockedCompareExchange(&value, desired, expected);
// 如果value == expected,则value = desired
// 返回原始值
// 64位原子操作(在64位系统上)
LONGLONG bigValue = 0;
InterlockedAdd64(&bigValue, 100);
// 指针原子操作
PVOID ptr = NULL;
PVOID newPtr = malloc(100);
PVOID oldPtr = InterlockedCompareExchangePointer(&ptr, newPtr, NULL);
内存屏障
c
// 编译器屏障(阻止编译器重排)
#define COMPILER_BARRIER() _ReadWriteBarrier()
// 内存屏障(阻止CPU重排)
void MemoryBarrierExample() {
volatile LONG flag = 0;
volatile LONG data = 0;
// 线程1:写入数据,然后设置标志
data = 42;
MemoryBarrier(); // 确保data写入在flag设置之前对其他CPU可见
flag = 1;
// 线程2:读取标志,然后读取数据
while (flag == 0) {
// 忙等待
}
MemoryBarrier(); // 确保flag读取在data读取之前
LONG value = data; // 保证看到42
}
第四部分:高级线程概念
1. 线程池(Thread Pool)
传统线程池API
c
#include <windows.h>
// 简单线程池任务
VOID CALLBACK ThreadPoolWorkCallback(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_WORK Work) {
int taskId = *(int*)Context;
printf("线程池执行任务 %d,线程ID: %d\n", taskId, GetCurrentThreadId());
Sleep(100);
}
void UseThreadPool() {
PTP_POOL pool = NULL;
PTP_WORK work = NULL;
TP_CALLBACK_ENVIRON callBackEnviron;
int taskData = 42;
// 创建线程池
pool = CreateThreadpool(NULL);
// 设置线程池参数
SetThreadpoolThreadMaximum(pool, 10); // 最大线程数
SetThreadpoolThreadMinimum(pool, 2); // 最小线程数
// 初始化回调环境
InitializeThreadpoolEnvironment(&callBackEnviron);
SetThreadpoolCallbackPool(&callBackEnviron, pool);
// 创建工作项
work = CreateThreadpoolWork(ThreadPoolWorkCallback, &taskData, &callBackEnviron);
// 提交工作到线程池
SubmitThreadpoolWork(work);
// 等待工作完成
WaitForThreadpoolWorkCallbacks(work, FALSE);
// 清理
CloseThreadpoolWork(work);
DestroyThreadpoolEnvironment(&callBackEnviron);
CloseThreadpool(pool);
}
Windows线程池组件
c
// 1. 工作项(Work)
PTP_WORK CreateThreadpoolWork(PTP_WORK_CALLBACK pfnwk, PVOID pv, PTP_CALLBACK_ENVIRON pcbe);
SubmitThreadpoolWork(PTP_WORK pwk);
WaitForThreadpoolWorkCallbacks(PTP_WORK pwk, BOOL fCancelPendingCallbacks);
// 2. 定时器(Timer)
PTP_TIMER CreateThreadpoolTimer(PTP_TIMER_CALLBACK pfnti, PVOID pv, PTP_CALLBACK_ENVIRON pcbe);
SetThreadpoolTimer(PTP_TIMER pti, PFILETIME pftDueTime, DWORD msPeriod, DWORD msWindowLength);
// 3. I/O完成项(I/O)
PTP_IO CreateThreadpoolIo(HANDLE h, PTP_WIN32_IO_CALLBACK pfnio, PVOID pv, PTP_CALLBACK_ENVIRON pcbe);
StartThreadpoolIo(PTP_IO pio);
CancelThreadpoolIo(PTP_IO pio);
// 4. 等待项(Wait)
PTP_WAIT CreateThreadpoolWait(PTP_WAIT_CALLBACK pfnwa, PVOID pv, PTP_CALLBACK_ENVIRON pcbe);
SetThreadpoolWait(PTP_WAIT pwa, HANDLE h, PFILETIME pftTimeout);
现代建议:使用PPL或TBB
cpp
// 使用C++并发运行时(Concurrency Runtime)
#include <ppl.h>
void UsePPL() {
// 并行for循环
Concurrency::parallel_for(0, 100, [](int i) {
printf("处理 %d\n", i);
});
// 任务组
Concurrency::task_group tasks;
tasks.run([] {
printf("任务1\n");
});
tasks.run([] {
printf("任务2\n");
});
tasks.wait(); // 等待所有任务完成
}
2. 纤程(Fiber)
纤程基础
c
VOID WINAPI FiberFunc(LPVOID lpParameter) {
printf("纤程开始执行\n");
// 纤程可以主动让出执行权
SwitchToFiber(GetCurrentFiber()); // 切换到主纤程
printf("纤程恢复执行\n");
// 转换回线程
ConvertFiberToThread();
}
void FiberExample() {
// 将当前线程转换为纤程
LPVOID mainFiber = ConvertThreadToFiber(NULL);
// 创建新纤程
LPVOID workerFiber = CreateFiber(
0, // 栈大小(0=默认)
FiberFunc, // 纤程函数
NULL // 参数
);
// 切换到工作纤程
SwitchToFiber(workerFiber);
// 切换回来后会继续执行这里
printf("回到主纤程\n");
// 再次切换
SwitchToFiber(workerFiber);
// 删除纤程
DeleteFiber(workerFiber);
// 将纤程转换回线程
ConvertFiberToThread();
}
纤程 vs 线程辨析
| 特性 | 线程 | 纤程 |
|---|---|---|
| 调度 | 内核调度,抢占式 | 用户调度,协作式 |
| 上下文切换 | 开销大(内核模式) | 开销小(用户模式) |
| 栈管理 | 内核管理 | 用户管理 |
| 并行性 | 真正并行(多核) | 单线程内并发 |
| 阻塞操作 | 阻塞线程,不影响其他线程 | 阻塞整个线程(所有纤程) |
| 适用场景 | CPU密集型,需要真并行 | I/O密集型,高并发连接 |
3. I/O完成端口(IOCP)
IOCP基础架构
c
#include <windows.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#define WORKER_THREAD_COUNT 4
HANDLE g_hCompletionPort;
DWORD WINAPI WorkerThread(LPVOID lpParam) {
DWORD bytesTransferred;
ULONG_PTR completionKey;
LPOVERLAPPED overlapped;
while (TRUE) {
// 从完成端口获取完成通知
BOOL success = GetQueuedCompletionStatus(
g_hCompletionPort,
&bytesTransferred,
&completionKey,
&overlapped,
INFINITE
);
if (!success) {
DWORD error = GetLastError();
if (error != WAIT_TIMEOUT) {
printf("GetQueuedCompletionStatus失败: %d\n", error);
break;
}
continue;
}
if (bytesTransferred == 0 && completionKey == 0) {
// 退出信号
break;
}
// 处理完成的I/O
printf("处理I/O完成: %lu字节\n", bytesTransferred);
}
return 0;
}
void SetupIOCP() {
// 创建完成端口
g_hCompletionPort = CreateIoCompletionPort(
INVALID_HANDLE_VALUE, // 文件句柄
NULL, // 现有完成端口
0, // 完成键
WORKER_THREAD_COUNT // 并发线程数
);
// 创建工作线程
HANDLE threads[WORKER_THREAD_COUNT];
for (int i = 0; i < WORKER_THREAD_COUNT; i++) {
threads[i] = CreateThread(NULL, 0, WorkerThread, NULL, 0, NULL);
}
// 关联文件/套接字到完成端口
// HANDLE hFile = CreateFile(...);
// CreateIoCompletionPort(hFile, g_hCompletionPort, (ULONG_PTR)hFile, 0);
// 发出异步I/O请求
// OVERLAPPED overlapped = {0};
// ReadFile(hFile, buffer, size, NULL, &overlapped);
}
第五部分:线程安全与最佳实践
1. 线程安全设计模式
线程局部存储模式
c
class ThreadLocalContext {
private:
DWORD m_tlsIndex;
public:
ThreadLocalContext() {
m_tlsIndex = TlsAlloc();
}
~ThreadLocalContext() {
TlsFree(m_tlsIndex);
}
void* Get() {
return TlsGetValue(m_tlsIndex);
}
void Set(void* value) {
TlsSetValue(m_tlsIndex, value);
}
};
双检查锁定模式(DCLP)
c
// 注意:标准DCLP在C/C++中可能存在问题(由于内存重排)
// 在C++11后使用std::atomic或std::call_once
class Singleton {
private:
static volatile Singleton* instance;
static CRITICAL_SECTION lock;
Singleton() {}
public:
static Singleton* GetInstance() {
if (instance == NULL) { // 第一次检查(不加锁)
EnterCriticalSection(&lock);
if (instance == NULL) { // 第二次检查(加锁)
instance = new Singleton();
}
LeaveCriticalSection(&lock);
}
return const_cast<Singleton*>(instance);
}
};
// 现代C++建议使用std::call_once或静态局部变量
2. 死锁预防与检测
死锁预防策略
c
// 1. 锁顺序协议
void ProcessA() {
EnterCriticalSection(&g_lock1);
EnterCriticalSection(&g_lock2);
// ...
LeaveCriticalSection(&g_lock2);
LeaveCriticalSection(&g_lock1);
}
void ProcessB() {
// 相同的锁顺序
EnterCriticalSection(&g_lock1);
EnterCriticalSection(&g_lock2);
// ...
LeaveCriticalSection(&g_lock2);
LeaveCriticalSection(&g_lock1);
}
// 2. 锁超时
BOOL TryEnterCriticalSectionWithTimeout(CRITICAL_SECTION* cs, DWORD timeout) {
DWORD start = GetTickCount();
while (GetTickCount() - start < timeout) {
if (TryEnterCriticalSection(cs)) {
return TRUE;
}
Sleep(10); // 避免忙等待
}
return FALSE;
}
// 3. 死锁检测(使用工具)
// - Windows任务管理器:查看线程阻塞
// - Process Explorer:查看线程等待链
// - Visual Studio调试器:并行堆栈视图
// - Windows性能分析器:并发分析
资源分配图算法实现(简化)
c
// 死锁检测数据结构
struct ResourceAllocationGraph {
std::vector<int> processes;
std::vector<int> resources;
std::map<int, std::vector<int>> allocation; // 资源->进程
std::map<int, std::vector<int>> request; // 进程->资源
};
bool DetectDeadlock(ResourceAllocationGraph& graph) {
// 银行家算法简化实现
std::vector<int> work = graph.resources;
std::vector<bool> finish(graph.processes.size(), false);
bool changed;
do {
changed = false;
for (size_t i = 0; i < graph.processes.size(); i++) {
if (!finish[i]) {
// 检查进程i的所有请求是否都能满足
bool canFinish = true;
for (int res : graph.request[i]) {
// 简化:假设每个资源只有一个实例
if (std::find(work.begin(), work.end(), res) == work.end()) {
canFinish = false;
break;
}
}
if (canFinish) {
// 释放进程占用的资源
for (int res : graph.allocation[i]) {
work.push_back(res);
}
finish[i] = true;
changed = true;
}
}
}
} while (changed);
// 检查是否所有进程都能完成
return std::find(finish.begin(), finish.end(), false) != finish.end();
}
3. 性能优化技巧
减少锁竞争
c
// 1. 锁分解(Lock Splitting)
class SplitLockResource {
CRITICAL_SECTION m_lock1;
CRITICAL_SECTION m_lock2;
Data m_data1;
Data m_data2;
public:
void Process1() {
EnterCriticalSection(&m_lock1);
// 只操作m_data1
LeaveCriticalSection(&m_lock1);
}
void Process2() {
EnterCriticalSection(&m_lock2);
// 只操作m_data2
LeaveCriticalSection(&m_lock2);
}
};
// 2. 锁粗化(Lock Coarsening)
void OptimizedFunction() {
EnterCriticalSection(&g_lock);
// 合并多个临界区为一个
Operation1();
Operation2();
Operation3();
LeaveCriticalSection(&g_lock);
}
// 3. 无锁数据结构
class LockFreeStack {
private:
struct Node {
int data;
Node* next;
};
Node* volatile m_head;
public:
void Push(int value) {
Node* newNode = new Node{value, nullptr};
Node* oldHead;
do {
oldHead = m_head;
newNode->next = oldHead;
} while (InterlockedCompareExchangePointer(
(PVOID*)&m_head,
newNode,
oldHead) != oldHead);
}
};
缓存友好设计
c
// 1. 伪共享(False Sharing)避免
struct alignas(64) CacheLineAlignedCounter {
volatile LONG counter; // 独占一个缓存行(通常64字节)
BYTE padding[64 - sizeof(LONG)];
};
CacheLineAlignedCounter g_counters[4]; // 每个CPU核心一个
// 2. 线程亲和性优化
void SetOptimalAffinity() {
DWORD_PTR processAffinity, systemAffinity;
GetProcessAffinityMask(GetCurrentProcess(),
&processAffinity,
&systemAffinity);
// 绑定线程到特定核心
SetThreadAffinityMask(GetCurrentThread(), 0x02); // CPU 1
}
// 3. NUMA优化
#include <numa.h> // Windows NUMA API
void NUMAOptimizedAlloc() {
// 在NUMA节点0上分配内存
PVOID memory = VirtualAllocExNuma(
GetCurrentProcess(),
NULL,
1024 * 1024, // 1MB
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE,
0 // NUMA节点
);
}
4. 调试与诊断工具
Windows调试工具集
c
// 1. 结构化异常处理(SEH)
__try {
// 可能引发异常的代码
int* ptr = NULL;
*ptr = 42; // 访问违规
}
__except(EXCEPTION_EXECUTE_HANDLER) {
printf("异常代码: 0x%08X\n", GetExceptionCode());
}
// 2. 向量化异常处理(VEH)
PVOID g_vehHandle = NULL;
LONG CALLBACK VectoredExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) {
printf("向量化异常: 0x%08X\n",
ExceptionInfo->ExceptionRecord->ExceptionCode);
return EXCEPTION_CONTINUE_SEARCH; // 继续搜索其他处理器
}
void InstallVEH() {
g_vehHandle = AddVectoredExceptionHandler(1, VectoredExceptionHandler);
}
// 3. 运行时检测
void EnableRuntimeChecks() {
// 调试堆
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
// 缓冲区安全检查
#pragma strict_gs_check(on)
}
性能分析API
c
// 1. 高精度计时
LARGE_INTEGER frequency, start, end;
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&start);
// 执行代码...
QueryPerformanceCounter(&end);
double elapsed = (end.QuadPart - start.QuadPart) * 1000.0 / frequency.QuadPart;
printf("执行时间: %.2f ms\n", elapsed);
// 2. 线程性能计数器
ULONG64 GetThreadCycleTime() {
ULONG64 cycleTime;
QueryThreadCycleTime(GetCurrentThread(), &cycleTime);
return cycleTime;
}
// 3. 性能监视器(PerfMon)计数器
void QueryPerformanceCounterExample() {
PDH_HQUERY query;
PDH_HCOUNTER counter;
PdhOpenQuery(NULL, NULL, &query);
PdhAddCounter(query, L"\\Processor(_Total)\\% Processor Time", NULL, &counter);
PdhCollectQueryData(query);
Sleep(1000);
PdhCollectQueryData(query);
PDH_FMT_COUNTERVALUE value;
PdhGetFormattedCounterValue(counter, PDH_FMT_DOUBLE, NULL, &value);
printf("CPU使用率: %.1f%%\n", value.doubleValue);
PdhCloseQuery(query);
}
第六部分:现代发展与未来趋势
1. C11/C17标准线程支持
C11线程API(如果编译器支持)
c
#include <threads.h>
#include <stdio.h>
int thread_func(void* arg) {
int id = *(int*)arg;
printf("C11线程 %d 运行\n", id);
return 0;
}
void use_c11_threads() {
thrd_t thread;
int id = 1;
if (thrd_create(&thread, thread_func, &id) == thrd_success) {
thrd_join(thread, NULL);
}
// C11互斥锁
mtx_t mutex;
mtx_init(&mutex, mtx_plain);
mtx_lock(&mutex);
mtx_unlock(&mutex);
mtx_destroy(&mutex);
}
2. 并行算法库
Windows并发运行时扩展
cpp
#include <ppl.h>
#include <vector>
void ParallelAlgorithms() {
std::vector<int> data(1000);
// 并行填充
Concurrency::parallel_for(0, 1000, [&](int i) {
data[i] = i * 2;
});
// 并行排序
Concurrency::parallel_sort(data.begin(), data.end());
// 并行归约
int sum = Concurrency::parallel_reduce(
data.begin(), data.end(), 0,
[](int total, int value) { return total + value; }
);
printf("总和: %d\n", sum);
}
3. GPU计算与异构编程
DirectCompute示例
cpp
// 简化的GPU计算概念
class GPUComputing {
public:
void RunComputeShader() {
// 1. 创建计算设备
// 2. 编译着色器
// 3. 创建缓冲区
// 4. 调度线程组
// 5. 读取结果
}
};
第七部分:总结
1. 技术选型
| 场景 | 推荐方案 | 替代方案 | 注意事项 |
|---|---|---|---|
| 简单并行任务 | 线程池(Thread Pool) | 手动创建线程 | 避免线程创建销毁开销 |
| I/O密集型 | I/O完成端口(IOCP) | 重叠I/O+线程池 | 适用于高并发连接 |
| CPU密集型 | OpenMP/TBB | 手动线程+工作窃取 | 注意负载均衡 |
| GUI应用 | 消息队列+工作线程 | 异步模式 | 主线程不阻塞UI |
| 跨进程同步 | 命名互斥量/事件 | 共享内存+信号量 | 注意权限和安全 |
| 读多写少 | SRW锁 | 读写锁模拟 | Vista+系统支持 |
2. 线程安全检查清单
☑ 所有共享数据都有适当的同步
☑ 避免全局变量,使用线程局部存储或封装
☑ 锁顺序一致,避免死锁
☑ 使用RAII管理锁资源(C++)
☑ 临界区尽可能短
☑ 避免在持有锁时调用未知函数
☑ 检查所有系统调用和库函数的线程安全性
☑ 正确处理线程退出和资源清理
☑ 考虑异常安全性
☑ 进行压力测试和竞态条件测试
3. 常见陷阱与解决方案
| 陷阱 | 现象 | 解决方案 |
|---|---|---|
| 资源泄漏 | 内存增长,句柄耗尽 | 使用RAII,确保每个Create都有对应的Close |
| 死锁 | 程序挂起,无响应 | 统一锁顺序,使用超时,死锁检测工具 |
| 竞态条件 | 随机崩溃,数据损坏 | 全面同步,原子操作,事务内存 |
| 优先级反转 | 高优先级线程饥饿 | 优先级继承,避免优先级倒置 |
| 缓存一致性 | 性能下降,数据不一致 | 内存屏障,缓存行对齐 |
| 虚假唤醒 | 条件变量提前唤醒 | 在循环中检查条件,而非if语句 |
觉得文章有帮助?别忘了:
👍 点赞 👍 - 给我一点鼓励
⭐ 收藏 ⭐ - 方便以后查看
🔔 关注 🔔 - 获取更新通知
标签: #C语言 #Windows编程 #多线程 #并发编程 #线程同步 #性能优化 #调试技巧 #现代C++