6.02 线程同步
原子锁
● 相关问题
多个线程对同一个数据进行原子操作(运算符操作),会产生结果丢失。比如执行++运算时
● 错误代码分析
当线程 A 执行 g_value ++时,如果线程切换时间正好是在线程 A 将值保存到 g_value 前,线程 B 继续执行 g_value ++,那么线程 A 再次被切换回来之后,会将之前线程 A 保存的 g_value 上,线程 B 进行的加法操作被覆盖
// 打断点 右键转到反汇编
// eax是寄存器
mov eax,dword ptr [g_value (07FF66681D170h)] // eax = g_value
inc eax // eax = eax + 1
mov dword ptr [g_value (07FF66681D170h)],eax // g_value = eax
// 如果在第二步切换,此时还未保存, 切到B线程后, 正常储存,再切回A,执行第三步,就会损失结果
● 使用原子锁函数
只能对操作符锁
- InterlockedIncrement(&g_valu) => g_value++
- InterlockedDecrement(&g_valu => g_value—
- InterlockedCompareExchange => 三目
- InterlockedExchanqe => 等于
原子锁的实现:直接对数据所在的内存操作,并且在任何一个瞬间只能有一个线程访问
互斥锁
● 相关的问题
多线程下代码或资源的共享使用
● 互斥的使用
- 任意时间点, 只能有一个线程拥有该互斥
- 所有线程都不拥有互斥, 有信号
1 创建互斥
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES IpMutexAttributes, //安全属性 置空
BOOL bInitialOwner//初始的拥有者 TRUE/FALSE
LPCTSTR IpName //命名
);创建成功返回互斥句柄(也是可等候句柄)
2 等候互斥
WaitFor…
如: WaitForSingleObject(mutex_handle, INFINITE);
互斥的等候遵循谁先等候谁先获取,
3 释放互斥
BOOL ReleaseMutex(
HANDLE hMutex // handle to mutex
);
4 关闭互斥句柄
CloseHandle
示例
HANDLE mutex_handle;
DWORD CALLBACK thread_func(LPVOID lpParam) {
WaitForSingleObject(mutex_handle, INFINITE);
// 访问被互斥的代码块
std::cout << "Hello, world!" << std::endl;
ReleaseMutex(mutex_handle);
return 0;
}
int main() {
mutex_handle = CreateMutex(NULL, FALSE, NULL);
HANDLE thread = CreateThread(NULL, 0, thread_func, NULL, 0, NULL);
if (thread == NULL) {
std::cerr << "Failed to create thread." << std::endl;
return 1;
}
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
CloseHandle(mutex_handle);
return 0;
}
事件
● 相关问题
程序(线程)之间的通知的问题。
● 事件的使用
△ 1 创建事件
也是可等候句柄,是否有信号,可以自行控制
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES IpEventAttributes,//安全属性
BOOL bManualReset, //事件重置(复位)方式,TRUE|FALSE (手动|自动), 有信号->无信号
BOOL bInitialState, //事件初始状态,TRUE 有信号
LPCTSTR IpName //事件命名
):创建成功返回事件句柄
△ 2 等候事件
WaitForSingleObject/WaitForMultipleObjects
△ 3 触发事件(将事件设置成有信号状态)
BOOL SetEvent(
HANDLE hEvent //handle to event
)
△ 4 复位事件(将事件设置成无信号状态)
BOOL ResetEvent(
HANDLE hEvent //handle to event
)
△ 5 关闭事件
CloseHandle
● 小心事件的死锁
信号量
● 相关的问题
类似于事件,解决通知的相关问题.但提供一个计数器,可以设置次数.
● 信号量的使用
1 创建信号量
也是可等候句柄,计数值为 0 时, 无信号
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES IpSemaphoreAttributes,//安全属性
LONG IInitialCount, // 信号量初始化数量
LONG IMaximumCount, // 信号量的最大值
LPCTSTR IpName // 命名
);创建成功返回信号量句柄
2 等候信号量
WaitFor…
每等候通过一次,信号量的信号减 1,直到为 0 时阻塞
3 给信号量指定计数值
BOOL ReleaseSemaphore(
HANDLE hSemaphore, //信号量句柄LONG
IReleaseCount, //释放数量,新的计数值
LPLONG IpPreviousCount //释放前原来信号量的数量,可以为NULL
)
4 关闭句柄
CloseHandle