C语言实现线程的之间的同步和互斥,通过临界区、互斥量、事件、信号量实现线程的同步互斥,同时讲解一下进程之间的同步互斥。
临界区实现同步互斥
-
首先我们要声明一个临界区的变量,利用临界区变量来实现临界区。
1
CRITICAL_SECTION global_cirtical_sectioin;
-
使用临界区变量需要先初始临界区变量。
1
InitializeCriticalSection(&global_cirtical_sectioin);
-
进入临界区,利用临界区变量上锁。
1
EnterCriticalSection(&global_cirtical_sectioin);
-
出临界区,解锁。
1
LeaveCriticalSection(&global_cirtical_sectioin);
-
使用结束后,删除临界变量。
1
DeleteCriticalSection(&global_cirtical_sectioin);
完成的使用流程:
1 | CRITICAL_SECTION global_cirtical_sectioin; |
这里的临界区的设置,只允许同一时间只有一个线程能够执行临界区代码。
互斥量实现同步互斥
大致步骤和临界区相同。
-
初始化互斥量。
1
HANDLE mutex = CreateMutex(NULL, FALSE, NULL);
CreateMutex
的作用是找出当前系统是否存在指定进程的示例,如果没有则创建一个互斥体。- 互斥对象是系统内核维护的一种数据结构,保证对象对单个线程的访问权。
- 互斥对象结构包括:使用数量(多少个线程调用该对象),线程ID(互斥对象的维护线程ID),计数器(当前线程调用该对象的次数)。
- 参数说明:
1
2
3
4
5CreateMutexA(
_In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes, // 指向安全属性的指针
_In_ BOOL bInitialOwner, // 初始化互斥对象的所有者 如果为 TRUE 表示互斥量为创建线程所有
_In_opt_ LPCSTR lpName // 指向互斥对象名的指针
); -
等待互斥量被触发。
1
WaitForSingleObject(mutex, INFINITE);
-
触发互斥量。
1
ReleaseMutex(mutex);
-
撤销互斥量。
1
CloseHandle(mutex);
事件实现同步互斥
-
初始化事件
1
HANDLE event = CreateEvent(NULL, false, false, NULL);
1
2
3
4
5
6HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全属性结构体指针
BOOL bManualReset, // 是否手动复位
BOOL bInitialState, // 初始状态
LPCTSTR lpName // 事件名称
);lpEventAttributes
:一个指向SECURITY_ATTRIBUTES结构体的指针,用于设置事件对象的安全属性。如果该参数为NULL,则事件对象会继承调用进程的安全属性。bManualReset
:一个布尔值,用于指定事件对象的类型。如果该参数为TRUE,则表示创建的是手动复位事件对象;如果该参数为FALSE,则表示创建的是自动复位事件对象。手动复位事件对象必须通过调用ResetEvent
函数来将事件状态复位(即重置为未激发状态);而自动复位事件对象则会在有信号触发时自动将其状态复位。bInitialState
:一个布尔值,用于指定事件对象的初始状态。如果该参数为TRUE,则表示在创建事件对象时立即将其设置为已激发状态(signaled);如果该参数为FALSE,则表示事件对象初始状态为未激发状态(nonsignaled)。lpName
:一个字符串指针,用于指定事件对象的名称。如果该参数为NULL,则表示创建一个无名事件对象;否则表示创建命名的事件对象。需要注意的是,如果同时存在同名的命名事件对象,则会返回该事件对象的句柄。
-
等待事件发生
1
WaitForSingleObject(event, INFINITE);
-
触发事件
1
SetEvent(event);
-
撤销事件
1
CloseHandle(event);
信号量同步
-
初始化信号量
1
2// 当前0个资源、最大允许1个同时访问
HANDLE semaphore = CreateSemaphore(NULL, 0, 1, NULL); -
等待$信号量>1$
1
WaitForSingleObject(semaphore, INFINITE);
-
释放一个资源,信号量加1
1
ReleaseSemaphore(semaphore, 1, NULL);
-
撤销信号量
1
CloseHandle(semaphore);
进程之间互斥信号量实现互斥
和线程之间原理一样,这里多了一步需要打开互斥对象,互斥对象有操作内核管理,通过名称查找到。
互斥对象存在时,同名的进程时不能运行的。
-
创建互斥量
1
HANDLE hMutex = CreateMutex(NULL, TRUE, MUTEX_NAME);
-
打开互斥量
1
HANDLE hMutex = OpenMutex(MUTEX_ALL_ACCESS, TRUE, MUTEX_NAME);
-
触发互斥量
1
ReleaseMutex(hMutex);
-
等待互斥量触发
1
WaitForSingleObject(hMutex, 10000);
存在问题
WaitForSingleObject()
WaitForSingleObject
函数的执行流程如下:
- 线程调用
WaitForSingleObject
函数并传入需要等待的对象的句柄以及等待时间的长度。 - 系统检查对象的当前状态:
- 如果对象已经是 signaled 状态(已激发),则直接返回
WAIT_OBJECT_0
,线程可以继续执行后续操作。 - 如果对象不是 signaled 状态(未激发),则线程进入等待状态,并将该线程从可执行状态转换为等待状态,直到以下三种情况之一发生:
- 对象被激发,即对象状态变为 signaled。此时,线程会被唤醒,并返回
WAIT_OBJECT_0
,线程可以继续执行后续操作。 - 等待超时,即指定的等待时间到达。此时,线程会被唤醒,并返回
WAIT_TIMEOUT
,线程可以根据需要进行相应处理。 - 等待的对象被放弃。这通常出现在使用互斥体时,当互斥体的所有者线程意外终止而没有正确释放互斥体时,其他线程在等待该互斥体时会返回
WAIT_ABANDONED
。
- 对象被激发,即对象状态变为 signaled。此时,线程会被唤醒,并返回
- 如果对象已经是 signaled 状态(已激发),则直接返回
- 线程根据返回值进行相应的处理。