Window编程(五)|线程

进程和线程区别

特性进程(Process)线程(Thread)
定义程序在操作系统中的一次执行实例进程内的一个执行单元,是 CPU 调度的基本单位
资源拥有独立的内存空间、文件句柄等系统资源共享所属进程的资源,仅拥有自己的栈和寄存器
创建开销高(需分配完整内存和系统资源)低(仅需创建线程栈和上下文)
通信方式通过 IPC(管道、消息队列、共享内存等)直接共享进程内存(需同步机制)
调度传统 OS 中是调度单位,现代 OS 中通常是线程现代 OS 中的基本调度单位
并发性进程间可并发执行(多核并行)同一进程的线程可并发执行(多核并行)
崩溃影响一个进程崩溃不影响其他进程同一进程的某个线程崩溃可能导致整个进程崩溃
上下文切换开销高(需切换内存映射、寄存器等)低(仅需切换线程栈和寄存器)
编程难度高(IPC 复杂,资源管理繁琐)中等(需处理同步和竞态条件)

常用函数

功能Windows APIPOSIX ThreadsC++11 标准库
创建线程CreateThreadpthread_createstd::thread
等待线程结束WaitForSingleObjectpthread_joinstd::thread::join
线程休眠Sleepsleep/usleepstd::this_thread::sleep
互斥锁CRITICAL_SECTIONpthread_mutex_*std::mutex
条件变量CreateEventpthread_cond_*std::condition_variable
原子操作InterlockedIncrement__sync_fetch_and_addstd::atomic
线程局部存储TlsAllocpthread_key_createthread_local

常用示例

以下是为各线程 API 示例添加的详细注释,解释代码逻辑和关键知识点:

一、线程创建与管理

1. Windows API

#include <windows.h>  // Windows API头文件
#include <iostream>

// 线程函数原型:必须返回DWORD,接受LPVOID参数
DWORD WINAPI ThreadFunction(LPVOID lpParam) {
    // 将参数转换为int类型
    int threadId = *(int*)lpParam;
    std::cout << "线程 " << threadId << " 正在运行" << std::endl;
    
    // 线程休眠1秒,模拟耗时操作
    Sleep(1000);  // 单位:毫秒
    
    std::cout << "线程 " << threadId << " 结束" << std::endl;
    return 0;  // 返回值可通过GetExitCodeThread获取
}

int main() {
    int id = 1;  // 传递给线程的参数
    
    // 创建线程
    HANDLE hThread = CreateThread(
        NULL,                   // 安全属性,NULL表示默认
        0,                      // 线程栈大小,0表示默认
        ThreadFunction,         // 线程函数地址
        &id,                    // 传递给线程函数的参数
        0,                      // 创建标志(如CREATE_SUSPENDED可挂起线程)
        NULL                    // 输出线程ID,NULL表示不获取
    );

    if (hThread == NULL) {
        std::cerr << "创建线程失败,错误码: " << GetLastError() << std::endl;
        return 1;
    }

    // 等待线程结束(INFINITE表示无限等待)
    WaitForSingleObject(hThread, INFINITE);

    // 关闭线程句柄(减少内核对象引用计数)
    CloseHandle(hThread);

    std::cout << "主线程结束" << std::endl;
    return 0;
}

二、线程同步机制

线程同步机制有多种方式可以实现,也有多套api函数可以用,比如Windows 使用CRITICAL_SECTION结构实现轻量级互斥;POSIX 标准使用pthread_mutex_t结构实现互斥锁;C++11 引入了跨平台的线程库,使用std::mutex类以及lock、unlock等函数来实现,本文主要讲Windows api函数。

1. 互斥锁(Mutex)

实现这样一个功能:创建四个线程,每个线程对变量sharedCounter执行1000000次+1,预期结果是4000000。可以预想到,如果没有互斥锁,会出现这种情况:线程1和线程2同时对100进行+1操作,最后写入101,(实际应该是102),最终结果会比预期结果少。

使用互斥锁的思路是:定义一个临界区,这个临界区的意义就相当于使用权,只有拿到使用权才能对sharedCounter执行+1操作,执行一次之后解锁,每次只允许一个线程进入临界区。这样就不会出现同时进行+1导致少加的情况出现。

加锁版:

#include <windows.h>
#include <iostream>
#include <vector>

CRITICAL_SECTION cs;    // 声明临界区
int sharedCounter = 0;  // 共享资源

DWORD WINAPI Worker(LPVOID iterations) {
    int iter = *(int*)iterations;
    for (int i = 0; i < iter; ++i) {
        EnterCriticalSection(&cs);      // 进入临界区(加锁)
        ++sharedCounter;                // 关键操作
        LeaveCriticalSection(&cs);      // 离开临界区(解锁)
    }
    return 0;
}

int main() {
    const int numThreads = 4;
    const int iterations = 1000000;
    std::vector<HANDLE> threads;

    InitializeCriticalSection(&cs);     // 初始化临界区

    // 创建多个线程
    for (int i = 0; i < numThreads; ++i) {
        int* arg = new int(iterations); // 为每个线程创建独立参数
        threads.push_back(CreateThread(
            NULL, 0, Worker, arg, 0, NULL
        ));
    }

    // 等待所有线程完成
    WaitForMultipleObjects(numThreads, threads.data(), TRUE, INFINITE);

    // 清理资源
    for (auto h : threads) CloseHandle(h);
    DeleteCriticalSection(&cs);         // 销毁临界区

    std::cout << "正确结果: " << sharedCounter 
              << " (预期: " << numThreads * iterations << ")" << std::endl;
    return 0;
}

结果:

无锁版:

#include <windows.h>
#include <iostream>
#include <vector>

int sharedCounter = 0;  // 共享资源,无任何同步保护

DWORD WINAPI Worker(LPVOID iterations) {
	int iter = *(int*)iterations;
	for (int i = 0; i < iter; ++i) {
		// 非原子操作,存在竞态条件
		++sharedCounter;  // 等价于:read-modify-write 三个步骤
	}
	return 0;
}

int main() {
	int numThreads = 4;
	int iterations = 1000000;
	std::vector<HANDLE> threads;

	// 创建多个线程同时修改共享资源
	for (int i = 0; i < numThreads; ++i) {
		threads.push_back(CreateThread(
			NULL, 0, Worker, &iterations, 0, NULL
		));
	}

	// 等待所有线程完成
	WaitForMultipleObjects(numThreads, threads.data(), TRUE, INFINITE);

	// 关闭线程句柄
	for (auto h : threads) {
		CloseHandle(h);
	}

	// 理论结果应为 numThreads * iterations = 4000000
	// 但实际结果通常小于预期
	std::cout << "无锁保护的结果: " << sharedCounter
		<< " (预期: " << numThreads * iterations << ")" << std::endl;

	return 0;
}

结果:

互斥体

互斥体原理跟锁差不多,区别在于:

互斥体(Mutex):是内核对象,可跨进程使用(如多个程序访问同一资源),性能开销较大。

临界区(Critical Section):是用户模式对象,只能在同一进程内使用,性能更优。

#include <windows.h>
#include <iostream>
#include <vector>

// 共享资源:计数器
int g_counter = 0;
// 互斥体句柄
HANDLE g_hMutex = NULL;

// 线程函数:增加计数器
DWORD WINAPI ThreadFunc(LPVOID lpParam) {
	// 传入参数:线程编号
	int threadId = *(int*)lpParam;

	// 循环10次,每次尝试获取互斥体并修改计数器
	for (int i = 0; i < 100000; i++) {
		// 等待互斥体(获取锁),无限等待
		WaitForSingleObject(g_hMutex, INFINITE); 
		// 安全访问共享资源
		int temp = g_counter;
		g_counter = temp + 1;

		// 释放互斥体(释放锁)
		ReleaseMutex(g_hMutex);

	}

	return 0;
}

int main() {
	const int THREAD_COUNT = 4;
	HANDLE hThreads[THREAD_COUNT];
	int threadIds[THREAD_COUNT] = { 1, 2 };

	// 创建互斥体(非初始拥有者,匿名互斥体)
	g_hMutex = CreateMutex(NULL, FALSE, NULL);
	if (g_hMutex == NULL) {
		std::cout << "创建互斥体失败,错误码: " << GetLastError() << std::endl;
		return 1;
	}

	// 创建多个线程
	for (int i = 0; i < THREAD_COUNT; i++) {
		hThreads[i] = CreateThread(
			NULL,               // 安全属性
			0,                  // 栈大小
			ThreadFunc,         // 线程函数
			&threadIds[i],      // 传入线程编号
			0,                  // 创建标志
			NULL                // 线程ID
		);

		if (hThreads[i] == NULL) {
			std::cout << "创建线程失败,错误码: " << GetLastError() << std::endl;
			// 关闭已创建的线程句柄
			for (int j = 0; j < i; j++) {
				CloseHandle(hThreads[j]);
			}
			// 关闭互斥体句柄
			CloseHandle(g_hMutex);
			return 1;
		}
	}

	// 等待所有线程结束
	WaitForMultipleObjects(THREAD_COUNT, hThreads, TRUE, INFINITE);

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

	// 关闭互斥体句柄
	CloseHandle(g_hMutex);

	std::cout << "最终计数器值: " << g_counter << std::endl;
	return 0;
}

如需测试反例,删去WaitForSingleObject(g_hMutex, INFINITE)和ReleaseMutex(g_hMutex)即可。

信号量

信号量跟互斥体差不多,只不过互斥体是内核提供,信号量是自定义,下述代码实现了限制游戏多开:

#include <windows.h>
#include <iostream>

int main() {
	// 创建命名信号量,初始计数为1,最大计数为1
	HANDLE hSemaphore = CreateSemaphore(
		NULL,          // 默认安全属性
		1,             // 初始计数(允许1个实例)
		1,             // 最大计数
		L"Global\\MyGameSemaphore"  // 信号量名称
	);

	// 检查是否创建失败(已存在)
	if (hSemaphore == NULL || GetLastError() == ERROR_ALREADY_EXISTS) {
		std::cout << "游戏已在运行,无法多开!" << std::endl;
		if (hSemaphore) CloseHandle(hSemaphore);
		return 1;
	}

	// 游戏主逻辑
	std::cout << "游戏启动成功!" << std::endl;
	std::cin.get();  // 等待用户输入,模拟游戏运行

	// 释放信号量并关闭句柄
	ReleaseSemaphore(hSemaphore, 1, NULL);
	CloseHandle(hSemaphore);
	return 0;
}

效果:

其他

除此之外,还有其他对象,不一一赘述,见表格:

同步对象Windows 实现Linux/POSIX 实现核心特性适用场景典型用法
互斥锁 (Mutex)CreateMutex/WaitForSingleObjectpthread_mutex_init/pthread_mutex_lock– 确保同一时间只有一个线程访问资源
– 支持所有权(同一线程可多次加锁)
– 可避免死锁(需正确释放)
保护共享数据(如全局变量、文件句柄)读取 / 修改数据库记录、文件写入
信号量 (Semaphore)CreateSemaphore/ReleaseSemaphoresem_open/sem_post– 控制同时访问资源的线程数量
– 通过计数器(初始值 / 最大值)实现限流
– 适用于有限资源管理
连接池、线程池、I/O 设备并发控制数据库连接数限制、网络请求并发数控制
条件变量 (Condition Variable)CreateConditionVariable/SleepConditionVariableCSpthread_cond_init/pthread_cond_wait– 线程等待特定条件满足时被唤醒
– 需配合互斥锁使用
– 解决 “忙等待” 问题,降低 CPU 消耗
生产者 – 消费者模型、异步任务通知任务队列处理、网络数据接收通知
临界区 (Critical Section)InitializeCriticalSection/EnterCriticalSection无(POSIX 用互斥锁替代)– 轻量级同步机制
– 仅适用于同一进程内线程
– 不可跨进程,效率高于互斥锁
进程内快速数据保护(如局部共享变量)函数内临时数据同步、小范围资源访问
读写锁 (Read-Write Lock)SRWLOCK(Windows 8+)pthread_rwlock_init– 允许多个读线程同时访问
– 写线程独占资源
– 优化 “多读少写” 场景的并发性能
配置文件读取、缓存数据访问日志系统读取、字典数据并发查询
屏障 (Barrier)InitializeThreadBarrierpthread_barrier_init– 确保多个线程在指定点同步
– 所有线程到达屏障后才继续执行
– 适用于并行计算同步
并行算法(如矩阵计算、图像渲染)多线程任务分阶段同步(如 MapReduce)
事件 (Event)CreateEvent/SetEventpthread_event(非标准,常用条件变量替代)– 线程间通信的信号机制
– 分为手动重置 / 自动重置事件
– 用于通知状态变化
异步操作完成通知、线程启动顺序控制网络请求回调通知、后台任务状态反馈
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇