学懂C++(三十):高级教程——深入解析 C++ Windows API 的多线程支持

引言

        在现代应用程序中,多线程编程是实现高性能和高并发任务的关键手段。Windows 操作系统为开发者提供了一套强大的 API,用于创建和管理线程、同步任务,并优化线程性能。本文将深入探讨 C++ 中 Windows API 的多线程支持,详细介绍线程的创建与控制、线程优先级的设置、线程局部存储(TLS)、各种同步机制(如临界区、互斥体、信号量、事件)以及纤程(Fibers)的应用。

1. 线程的创建和控制

1.1 创建线程:CreateThread

CreateThread 是 Windows API 用于创建线程的函数。它允许程序在不同的并发路径上运行代码,从而提升应用程序的响应速度和处理能力。

示例:创建一个简单的线程
#include <windows.h>
#include <iostream>

DWORD WINAPI ThreadFunc(LPVOID lpParam) {
    std::cout << "Thread is running." << std::endl;
    return 0;
}

int main() {
    HANDLE hThread = CreateThread(
        nullptr,         // 默认安全属性
        0,               // 默认堆栈大小
        ThreadFunc,      // 线程函数
        nullptr,         // 线程函数参数
        0,               // 默认创建标志
        nullptr          // 不返回线程标识符
    );

    if (hThread) {
        WaitForSingleObject(hThread, INFINITE); // 等待线程完成
        CloseHandle(hThread); // 关闭线程句柄
    }

    return 0;
}

1.2 线程退出与终止:ExitThread 和 TerminateThread

  • ExitThread:用于安全地退出线程。
  • TerminateThread:强制终止线程,但会导致资源泄漏,不推荐使用。
示例:使用 ExitThread 安全退出线程
#include <windows.h>
#include <iostream>

DWORD WINAPI ThreadFunc(LPVOID lpParam) {
    std::cout << "Thread is running." << std::endl;
    ExitThread(0);  // 安全退出线程
}

int main() {
    HANDLE hThread = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, nullptr);

    if (hThread) {
        WaitForSingleObject(hThread, INFINITE); // 等待线程完成
        CloseHandle(hThread); // 关闭线程句柄
    }

    return 0;
}
示例:使用 TerminateThread 强制终止线程(不推荐)
#include <windows.h>
#include <iostream>

DWORD WINAPI ThreadFunc(LPVOID lpParam) {
    while (true) {
        std::cout << "Thread is running." << std::endl;
        Sleep(1000);
    }
    return 0;
}

int main() {
    HANDLE hThread = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, nullptr);

    if (hThread) {
        Sleep(3000); // 让线程运行一段时间
        TerminateThread(hThread, 0); // 强制终止线程(不推荐)
        CloseHandle(hThread); // 关闭线程句柄
    }

    return 0;
}

1.3 等待线程完成:WaitForSingleObject 和 WaitForMultipleObjects

  • WaitForSingleObject:用于等待单个线程或同步对象的信号。
  • WaitForMultipleObjects:用于等待多个线程或同步对象的信号。
示例:使用 WaitForSingleObject 等待单个线程
#include <windows.h>
#include <iostream>

DWORD WINAPI ThreadFunc(LPVOID lpParam) {
    Sleep(2000); // 模拟长时间运行的操作
    std::cout << "Thread completed." << std::endl;
    return 0;
}

int main() {
    HANDLE hThread = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, nullptr);

    if (hThread) {
        std::cout << "Waiting for thread to complete..." << std::endl;
        WaitForSingleObject(hThread, INFINITE); // 等待线程完成
        std::cout << "Thread has completed." << std::endl;
        CloseHandle(hThread); // 关闭线程句柄
    }

    return 0;
}
示例:使用 WaitForMultipleObjects 等待多个线程
#include <windows.h>
#include <iostream>

DWORD WINAPI ThreadFunc(LPVOID lpParam) {
    Sleep(1000); // 模拟操作
    std::cout << "Thread " << *(int*)lpParam << " completed." << std::endl;
    return 0;
}

int main() {
    HANDLE hThreads[2];
    int threadParams[2] = { 1, 2 };

    for (int i = 0; i < 2; i++) {
        hThreads[i] = CreateThread(nullptr, 0, ThreadFunc, &threadParams[i], 0, nullptr);
    }

    std::cout << "Waiting for threads to complete..." << std::endl;
    WaitForMultipleObjects(2, hThreads, TRUE, INFINITE); // 等待所有线程完成

    for (int i = 0; i < 2; i++) {
        CloseHandle(hThreads[i]); // 关闭线程句柄
    }

    std::cout << "All threads have completed." << std::endl;
    return 0;
}

2. 线程优先级

Windows 提供了丰富的 API 来控制线程的优先级,以便更好地管理线程的执行顺序。

2.1 设置线程优先级:SetThreadPriority

SetThreadPriority 允许开发者根据线程的重要性来调整其优先级,从而影响调度器对线程的调度频率。

示例:设置线程的优先级
#include <windows.h>
#include <iostream>

DWORD WINAPI ThreadFunc(LPVOID lpParam) {
    std::cout << "Thread is running at priority: " << GetThreadPriority(GetCurrentThread()) << std::endl;
    return 0;
}

int main() {
    HANDLE hThread = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, nullptr);
    
    if (hThread) {
        SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST); // 设置线程优先级
        WaitForSingleObject(hThread, INFINITE);
        CloseHandle(hThread);
    }

    return 0;
}

2.2 获取线程优先级:GetThreadPriority

GetThreadPriority 用于获取当前线程的优先级,以便对线程的执行情况进行监控或调整。

示例:获取线程优先级
#include <windows.h>
#include <iostream>

DWORD WINAPI ThreadFunc(LPVOID lpParam) {
    std::cout << "Thread is running at priority: " << GetThreadPriority(GetCurrentThread()) << std::endl;
    return 0;
}

int main() {
    HANDLE hThread = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, nullptr);
    
    if (hThread) {
        int priority = GetThreadPriority(hThread);
        std::cout << "Initial thread priority: " << priority << std::endl;

        SetThreadPriority(hThread, THREAD_PRIORITY_LOWEST); // 改变线程优先级
        priority = GetThreadPriority(hThread);
        std::cout << "Changed thread priority: " << priority << std::endl;

        WaitForSingleObject(hThread, INFINITE);
        CloseHandle(hThread);
    }

    return 0;
}

3. 线程局部存储(TLS)

线程局部存储(TLS)允许线程在其本地范围内存储和访问数据。每个线程都有独立的存储区域,不会与其他线程共享。

3.1 分配 TLS 索引:TlsAlloc

TlsAlloc 分配一个 TLS 索引,供后续在该索引上存取数据。

3.2 访问 TLS 数据:TlsGetValue 和 TlsSetValue

  • TlsGetValue:从指定的 TLS 索引获取数据。
  • TlsSetValue:在指定的 TLS 索引上存储数据。

3.3 释放 TLS 索引:TlsFree

TlsFree 释放先前分配的 TLS 索引。

示例:使用 TLS 存储线程局部数据
#include <windows.h>
#include <iostream>

DWORD tlsIndex;

DWORD WINAPI ThreadFunc(LPVOID lpParam) {
    int localData = *(int*)lpParam; // 每个线程独立的数据
    TlsSetValue(tlsIndex, (LPVOID)&localData);

    int* pData = (int*)TlsGetValue(tlsIndex);
    std::cout << "Thread local data: " << *pData << std::endl;

    return 0;
}

int main() {
    tlsIndex = TlsAlloc(); // 分配 TLS 索引
    if (tlsIndex == TLS_OUT_OF_INDEXES) {
        return 1;
    }

    int threadData[2] = { 10, 20 };
    HANDLE hThread1 = CreateThread(nullptr, 0, ThreadFunc, &threadData[0], 0, nullptr);
    HANDLE hThread2 = CreateThread(nullptr, 0, ThreadFunc, &threadData[1], 0, nullptr);

    WaitForSingleObject(hThread1, INFINITE);
    WaitForSingleObject(hThread2, INFINITE);

    CloseHandle(hThread1);
    CloseHandle(hThread2);

    TlsFree(tlsIndex); // 释放 TLS 索引
    return 0;
}

4. 同步机制

4.1 临界区(Critical Section)

临界区用于保护共享资源,防止多个线程同时访问同一资源。相比互斥体,临界区的性能更高,但只适用于单一进程内的线程同步。

  • InitializeCriticalSection:初始化临界区。
  • EnterCriticalSection:进入临界区。
  • LeaveCriticalSection:离开临界区。
  • DeleteCriticalSection:删除临界区。
示例:使用临界区保护共享资源
#include <windows.h>
#include <iostream>

CRITICAL_SECTION cs;
int counter = 0;

DWORD WINAPI ThreadFunc(LPVOID lpParam) {
    for (int i = 0; i < 10000; ++i) {
        EnterCriticalSection(&cs);
        ++counter;
        LeaveCriticalSection(&cs);
    }
    return 0;
}

int main() {
    InitializeCriticalSection(&cs);

    HANDLE hThread1 = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, nullptr);
    HANDLE hThread2 = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, nullptr);

    WaitForSingleObject(hThread1, INFINITE);
    WaitForSingleObject(hThread2, INFINITE);

    std::cout << "Final counter value: " << counter << std::endl;

    CloseHandle(hThread1);
    CloseHandle(hThread2);
    DeleteCriticalSection(&cs);
    return 0;
}

4.2 互斥体(Mutex)

互斥体是用于多进程同步的对象。它比临界区更通用,但性能略低。

  • CreateMutex:创建或打开一个命名的互斥体对象。
  • ReleaseMutex:释放互斥体。
示例:使用互斥体同步线程
#include <windows.h>
#include <iostream>

HANDLE hMutex;

DWORD WINAPI ThreadFunc(LPVOID lpParam) {
    WaitForSingleObject(hMutex, INFINITE); // 请求互斥体
    std::cout << "Thread running with mutex protection." << std::endl;
    Sleep(1000); // 模拟工作
    ReleaseMutex(hMutex); // 释放互斥体
    return 0;
}

int main() {
    hMutex = CreateMutex(nullptr, FALSE, nullptr); // 创建互斥体

    HANDLE hThread1 = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, nullptr);
    HANDLE hThread2 = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, nullptr);

    WaitForSingleObject(hThread1, INFINITE);
    WaitForSingleObject(hThread2, INFINITE);

    std::cout << "Both threads have completed." << std::endl;

    CloseHandle(hThread1); // 关闭线程句柄
    CloseHandle(hThread2);
    CloseHandle(hMutex); // 关闭互斥体句柄

    return 0;
}

4.3 信号量(Semaphore)

信号量控制一组资源的访问权限,可以限制多个线程同时访问一个或多个资源。

  • CreateSemaphore:创建信号量对象。
  • ReleaseSemaphore:释放信号量。
示例:使用信号量控制线程访问资源
#include <windows.h>
#include <iostream>

HANDLE hSemaphore;

DWORD WINAPI ThreadFunc(LPVOID lpParam) {
    WaitForSingleObject(hSemaphore, INFINITE); // 请求信号量
    std::cout << "Thread is accessing a limited resource." << std::endl;
    Sleep(1000); // 模拟工作
    ReleaseSemaphore(hSemaphore, 1, nullptr); // 释放信号量
    return 0;
}

int main() {
    hSemaphore = CreateSemaphore(nullptr, 2, 2, nullptr); // 创建信号量,初始值和最大值均为2

    HANDLE hThread1 = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, nullptr);
    HANDLE hThread2 = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, nullptr);
    HANDLE hThread3 = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, nullptr); // 第三个线程将等待

    // 等待所有线程完成
    WaitForSingleObject(hThread1, INFINITE);
    WaitForSingleObject(hThread2, INFINITE);
    WaitForSingleObject(hThread3, INFINITE);

    std::cout << "All threads have completed." << std::endl;

    CloseHandle(hThread1);
    CloseHandle(hThread2);
    CloseHandle(hThread3);
    CloseHandle(hSemaphore); // 关闭信号量句柄

    return 0;
}

4.4 事件(Event)

事件对象用于线程之间的信号传递,允许一个线程通知一个或多个线程某个事件的发生。

  • CreateEvent:创建事件对象。
  • SetEvent:设置事件为信号状态。
  • ResetEvent:将事件重置为非信号状态。
示例:使用事件同步线程
#include <windows.h>
#include <iostream>

HANDLE hEvent;

DWORD WINAPI ThreadFunc(LPVOID lpParam) {
    std::cout << "Thread waiting for event..." << std::endl;
    WaitForSingleObject(hEvent, INFINITE); // 等待事件被设置
    std::cout << "Thread proceeding after event!" << std::endl;
    return 0;
}

int main() {
    hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); // 创建手动重置事件
    HANDLE hThread = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, nullptr);
    
    if (hThread) {
        Sleep(2000); // 模拟一些工作
        std::cout << "Setting event." << std::endl;
        SetEvent(hEvent); // 释放线程
        WaitForSingleObject(hThread, INFINITE); // 等待线程完成
        CloseHandle(hThread);
    }

    CloseHandle(hEvent); // 关闭事件句柄
    return 0;
}

5. 纤程(Fibers)

纤程是比线程更轻量级的执行单元,允许程序在用户空间中管理调度。纤程的创建和切换比线程更快,适合某些特定的高性能应用。

5.1 纤程的基本操作

  • ConvertThreadToFiber:将当前线程转换为纤程。
  • CreateFiber:创建纤程。
  • SwitchToFiber:切换到指定的纤程。
  • DeleteFiber:删除纤程。
示例:使用纤程
#include <windows.h>
#include <iostream>

VOID CALLBACK FiberFunc(ULONG_PTR lpParam) {
    std::cout << "Fiber is running!" << std::endl;
}

int main() {
    // 将主线程转换为纤程
    PVOID pFiber = ConvertThreadToFiber(nullptr);
    if (pFiber == nullptr) {
        std::cerr << "Failed to convert thread to fiber." << std::endl;
        return 1;
    }

    // 创建新的纤程
    PVOID pNewFiber = CreateFiber(0, FiberFunc, nullptr);
    if (pNewFiber == nullptr) {
        std::cerr << "Failed to create fiber." << std::endl;
        return 1;
    }

    // 切换到新的纤程
    SwitchToFiber(pNewFiber);

    // 切换回原纤程
    SwitchToFiber(pFiber);

    // 删除新创建的纤程
    DeleteFiber(pNewFiber);

    return 0;
}

6. 总结

        通过以上内容,我们详细展示了 C++ 中 Windows API 的多线程支持,包括线程的创建、控制、优先级管理、线程局部存储、各种同步机制(临界区、互斥体、信号量、事件)以及纤程的使用。每个示例都展示了如何使用相关的 Windows API,帮助你加深理解和掌握多线程编程的技巧。希望这些示例能够帮助你在开发高性能和高并发的应用程序时更得心应手。通过掌握这些技术,开发者能够构建高效、稳定的多线程应用,充分利用现代硬件的并发能力,提升应用程序的性能。希望本文能够为 C++ 开发者提供有益的指导和实践经验,助力提升在 Windows 平台下的多线程编程能力。

上一篇:学懂C++(二十九):高级教程——深入解析 C++ 异步任务和 Futures:std::future、

下一篇:学懂C++(三十一):高级教程——深入详解C++高级多线程编程技术之锁优化与替代 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猿享天开

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值