第三部分:内核区同步
等待函数(WaitForObject)
等待函数的形式
- 单个:WaitForSingleObject
- 多个:WaitForMultipleObjects
一个可以被等待的对象通常由两种状态,分别是:
- 可等待(激发态)(有信号):等待函数【不会阻塞】
- 不可等待(非激发态)(无信号):等待函数需要等待一定时长并【阻塞】
等待函数的副作用:
- 改变被等待对象的信号状态
#include <iostream> #include <windows.h> ? // 工作线程 DWORD WINAPI WorkerThread(LPVOID lpThreadParameter) { // 输出 0 ~ 99 for (int i = 0; i < 1000; ++i) { printf("%d\n", i); } ? return 0; } ? int main() { HANDLE Threads[3] = { 0 }; ? // 创建两个线程 Threads[0] = CreateThread(NULL, NULL, WorkerThread, NULL, NULL, NULL); Threads[1] = CreateThread(NULL, NULL, WorkerThread, NULL, NULL, NULL); Threads[2] = CreateThread(NULL, NULL, WorkerThread, NULL, NULL, NULL); ? // 函数返回的两种情况 // 1. 被等待对象处于有信号状态 // 2. 等到超时函数会返回,但是等待失败 ? // 等待两个线程退出 // 1. 一个可以被等待的内核对象 // 2. 等待时长,如果是-1 表示一直等待 // WaitForSingleObject(ThreadHandles[0], INFINITE); // WaitForSingleObject(ThreadHandles[1], INFINITE); ? // 等待多个内核对象 // 1. 需要等待多少个内核对象 // 2. 内核对象句柄的数组 // 3. 是否等待所有内核对象编程变成有信号的 // 4. 等待的时长,单位是毫秒 // WaitForMultipleObjects(2, ThreadHandles, TRUE, INFINITE); ? // 有任何一个执行完毕,另外一个就不执行了 WaitForMultipleObjects(3, Threads, FALSE, INFINITE); ? // 等待函数的副作用:修改被等待对象的信号状态 ? return 0; }
互斥体(Mutex)
- 特点:拥有临界区的线程拥有者概念,但是线程崩溃不会死锁,执行较慢
- 函数
- 创建:CreateMutex,如果第二个参数是FALSE,那么就是可等待
- 打开:OpenMutex
- 保护:WaitForSingleObject: 把互斥体置为不可等待
- 释放:ReleaseMutex把互斥体置为可等待
- 关闭:CloseHandle
// 1. 创建一个互斥体内核对象 // - 安全属性,初始状态,名称 HANDLE Mutex = CreateMutex(NULL, FALSE, L"Mutex"); ? DWORD WINAPI ThreadPro1(LPVOID lpThreadParameter) { // 为 g_Number 自增 100000 次 for (int i = 0; i < 100000; i++) { // 2. 使用等待函数对互斥体进行等待,当互斥体处于激发态就会等待成功 // 当等待成功时,会将互斥体置为无信号状态 WaitForSingleObject(Mutex, INFINITE); g_Number++; // 3. 执行完代码之后需要恢复有信号状态 ReleaseMutex(Mutex); } return 0; }
事件(Event)
- 特点:可以手动选择是否产生副作用,如果设置为手动状态,那么就不产生副作用
- 函数:
- 创建:CreateEvent()
- 打开:OpenEvent()
- 保护:WaitForSingleObject()
- 设置为非激发态:ResetEvent()
- 退出(设置为激发态):SetEvent()
- 关闭:CloseHandle
// 1. 创建一个事件内核对象 // - 安全属性,是否自动,初始状态,名称 HANDLE Event = CreateEvent(NULL, FALSE, TRUE, L"Event"); // HANDLE Event = CreateEvent(NULL, TRUE, TRUE, L"Event"); ? DWORD WINAPI ThreadPro1(LPVOID lpThreadParameter) { // 为 g_Number 自增 100000 次 for (int i = 0; i < 100000; i++) { // 注意:如果使用手动模式,等待函数就没有副作用 // 可以使用 ResetEvent 手动设置信号状态,但是没有意义 // 原因是 ResetEvent 本身不是一个原子操作 ? // 2. 使用等待函数对事件进行等待,当事件处于激发态就会等待成功 // 当等待成功时,会将事件置为无信号状态 WaitForSingleObject(Event, INFINITE); g_Number++; // 3. 执行完代码之后需要恢复有信号状态 SetEvent(Event); } return 0; }
信号量
- 特点:有多把锁,可以控制活动线程的数量,没有线程拥有者概念。
- 如果锁的最大个数是1,则用法和互斥体类似
- 函数:
- 创建:CreateSemaphore
- 打开:OpenSemaphore
- 上锁:WaitForSingleObject
- 解锁:ReleaseSemaphore
- 关闭:CloseHandle
// 1. 创建一个信号量内核对象 // - 安全属性,当前信号,最大信号,名称 HANDLE Semaphore = CreateSemaphore(NULL, 1, 1, L"Semaphore"); ? DWORD WINAPI ThreadPro1(LPVOID lpThreadParameter) { // 为 g_Number 自增 100000 次 for (int i = 0; i < 100000; i++) { // 2. 使用等待函数对信号量进行等待,当信号个数>1表示可等待 // 当等待成功时,会将信号个数 -1 WaitForSingleObject(Semaphore, INFINITE); g_Number++; // 3. 执行完代码之后需要将信号个数 +1 // 信号量对象,+几个信号,之前有多少个信号 ReleaseSemaphore(Semaphore, 1, NULL); } return 0; }
原文地址:https://www.cnblogs.com/ltyandy/p/10938205.html
时间: 2024-10-14 00:13:46