一、锁和临界区差异
他们都是实现线程同步和互斥访问共享资源的机制。从几个方面来介绍一下他们的区别;
1、定义差异
临界区:每个进程中访问临界资源的那段代码称为临界区,临界区不是内核对象,而是系统提供的一种数据结构。程序中可以声明一个该类型的变量,之后用它来实现对资源的互斥访问。当欲访问某一临界资源时,先将该临界区加锁(若临界区不空闲则等待),用完该资源后,将临界区释放
锁(互斥锁Mutex):锁是一种更通用的同步机制,用于保护临界区,确保同一个时刻只有一个线程可以访问共享资源;锁可以是内核对象,也可以是用户态对象,它可以在不同进程的多个线程之间同步。
2、工作模式差异
临界区:工作在用户用户模式下,仅限与同一个进程内。
锁:可以是内核对象,也可以在用户态进行操作。作为内核对象时,锁可以在不同进程的多个线程之间进行同步
3、性能差异
临界区:在用户态进行锁操作,因此速度较快
锁:作为内核对象的锁,在进行多线同步时速度较慢,因为涉及到内核态和用户态之间的切换
4、实现差异
临界区:依赖于操作系统提供的API,如Windows中的CRITICAL_SECTION
结构
锁:锁的实现可以更加灵活,可以是简单的二元锁(如互斥锁),也可以是更复杂的锁机制,如读写锁、条件变量等
二、其它替代方法
1、原子操作
原子操作确保了单个操作的不可分割性,适用于简单的线程同步场景,如对共享变量的原子更新。
C++11引入了原子操作库<atomic>
,提供了一种无需使用锁的线程安全编程方式。
2、条件变量
条件变量用于在某些条件不满足时挂起线程,并在条件满足时被唤醒。它通常与互斥锁一起使用,以实现更复杂的同步逻辑。
C++11中的<condition_variable>
头文件提供了条件变量的支持
3、信号量
信号量用于控制对共享资源的访问数量。它允许多个线程在同一时刻访问同一资源,但需要限制在同一时刻访问此资源的最大线程数目。
C++11中的<semaphore>
头文件提供了信号量的支持。
4、事件
事件是一种同步机制,用于通知线程某个事件已经发生。它可以用来实现线程间的协调和同步。
三、使用demo
1、临界区
#include <windows.h>
#include <iostream>
// 全局临界区对象
CRITICAL_SECTION g_cs;
// 共享资源
int sharedResource = 0;
// 线程函数,增加共享资源
void AddResource(void* param) {
EnterCriticalSection(&g_cs); // 进入临界区
std::cout << "Adding to resource..." << std::endl;
sharedResource += 1; // 修改共享资源
std::cout << "Resource value: " << sharedResource << std::endl;
LeaveCriticalSection(&g_cs); // 离开临界区
}
// 线程函数,读取共享资源
void ReadResource(void* param) {
EnterCriticalSection(&g_cs); // 进入临界区
std::cout << "Reading resource..." << std::endl;
std::cout << "Resource value: " << sharedResource << std::endl;
LeaveCriticalSection(&g_cs); // 离开临界区
}
int main() {
// 初始化临界区
InitializeCriticalSection(&g_cs);
// 创建两个线程,一个增加资源,一个读取资源
HANDLE hThreadAdd = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)AddResource, NULL, 0, NULL);
HANDLE hThreadRead = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ReadResource, NULL, 0, NULL);
// 等待线程结束
WaitForSingleObject(hThreadAdd, INFINITE);
WaitForSingleObject(hThreadRead, INFINITE);
// 清理临界区
DeleteCriticalSection(&g_cs);
// 关闭线程句柄
CloseHandle(hThreadAdd);
CloseHandle(hThreadRead);
return 0;
}
2、锁
// std::mutex
// std::lock_guard
// std::unique_lock,
std::mutex tx;