C语言并发编程:Windows线程

本文献给:

已掌握C语言基础,希望全面理解Windows环境下多线程编程的开发者。本文将深入讲解Windows线程的核心概念、高级特性、同步机制,并对相似概念进行辨析,帮助您构建一个稳健的并发程序。

你将学到:

  1. 线程与进程的对比及现代系统架构
  2. Windows线程的完整生命周期管理与控制
  3. 多种线程同步机制的实现
  4. 线程池、纤程等高级并发概念
  5. 性能优化、调试技巧与实践

**本文适用平台:**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等
线程 进程内的执行流,共享进程资源 共享进程内存和资源,拥有独立栈、寄存器、线程本地存储 小(仅需分配栈和线程控制块) 直接共享内存(需同步)、消息队列

现代系统架构演进

传统模型: 一个进程对应一个线程(单线程进程)
现代模型: 一个进程包含多个线程(多线程进程)
发展趋势:

  1. **异步编程模型:**I/O完成端口、异步I/O
  2. **用户态调度:**纤程(Fiber)、协程
  3. **硬件并发:**SIMD指令集、GPU计算
  4. **微内核设计:**更小的进程上下文切换开销

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++

相关推荐
Facechat20 分钟前
视频混剪-性能优化
性能优化·音视频
ChoSeitaku23 分钟前
16.C++入门:list|手撕list|反向迭代器|与vector对比
c++·windows·list
酒醉的胡铁26 分钟前
Docker Desktop 数据迁移完整流程(Windows 10/11 x64)
windows·docker·容器
客卿12328 分钟前
C语言刷题--合并有序数组
java·c语言·算法
Qhumaing29 分钟前
C++学习:【PTA】数据结构 7-1 实验6-1(图-邻接矩阵)
c++·学习·算法
No0d1es29 分钟前
2025年12月 GESP CCF编程能力等级认证C++一级真题
开发语言·c++·青少年编程·gesp·ccf
卓码软件测评34 分钟前
CMA-CNAS软件测评报告机构【Apifox动态Mock响应处理复杂业务逻辑设计】
测试工具·性能优化·单元测试·测试用例
2301_7737303136 分钟前
系统编程—在线商城信息查询系统
c++·html
郝学胜-神的一滴37 分钟前
深入理解Linux中的Try锁机制
linux·服务器·开发语言·c++·程序人生
无限码力38 分钟前
华为OD机试真题双机位C卷 【运维日志排序】C语言实现
c语言·华为od·华为od机考·华为od机试真题·华为od机试双机位c卷·华为od机考双机位c卷·华为od上机考试