线程的互斥与同步
线程的互斥与同步
互斥:
当多个线程访问同一个全局变量,或者同一个资源(比如打印机)的时候,需要进行线程间的互斥操作来保证访问的安全性。
临界区、互斥体、事件和信号量都可以实现线程互斥.但如果仅仅需要实现互斥功能,推荐前两种:
两者的区别:
1、临界区只能用于进程内的线程互斥,性能较好.
2、互斥体属于内核对象,可以用于进程间的线程互斥,性能较差.
3、线程在没有正常退出互斥区而意外终结时,互斥体可以复位,但临界区不行.
同步:
当有多个线程同时执行时,可能需要线程按照一定的顺序执行,比如:线程A负责将要处理的数据读取到内存中,而线程B负责分析这些数据,此时,应该是线程A执行完毕再执行线程B才有意义,这个时候就需要进行线程的同步控制。
可以用于线程同步控制的对象:事件和信号量
两者的区别:
1、都是内核对象,使用完毕后应该关闭句柄.
2、信号量可以用于相当复杂的线程同步控制.
临界区的使用:
1、创建CRITICAL_SECTION:
CRITICAL_SECTION cs;
2、在使用前进行初始化
InitializeCriticalSection(&cs);
3、在函数中使用:
DWORD WINAPI 线程A(PVOID pvParam)
{
EnterCriticalSection(&cs);
//对全局遍历X的操作
LeaveCriticalSection(&cs);
return(0);
}
DWORD WINAPI 线程B(PVOID pvParam)
{
EnterCriticalSection(&g_cs);
//对全局遍历X的操作
LeaveCriticalSection(&g_cs);
return(0);
}
4、删除CRITICAL_SECTION
VOID DeleteCriticalSection(PCRITICAL_SECTION pcs);
当线程不再试图访问共享资源时
互斥体的使用:
HANDLE CreateMutexW(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner, // False:创建后,能直接使用(有信号)
// True: 创建后,不能直接使用(没信号),表示当前互斥体属于当前的进程
LPCWSTR lpName // 给内核互斥体命名
);
获取互斥体令牌的方式:
-
有信号
-
线程的拥有者 (即使参数为 True,A进程仍能获取互斥体,但 B进程无法获取)
// 互斥体.cpp (A进程)
int main() {
// 创建一个互斥体
HANDLE hMutex = CreateMutex(NULL, FALSE, L"XYZ");
// 获取互斥体
WaitForSingleObject(hMutex, INFINITE);
for (int i = 0; i < 10; i++) {
Sleep(1000);
printf(“A进程的X线程 %d\n”, i);
}
// 释放互斥体
ReleaseMutex(hMutex);
return 0;
}
// Test.cpp (B进程)
int main() {
HANDLE hMutex = CreateMutex(NULL, FALSE, L"XYZ");
// 这个互斥体创建失败,因为 A进程已经创建了一个同名互斥体
// 但仍会返回同名互斥体的句柄
WaitForSingleObject(hMutex, INFINITE);
for (int i = 0; i < 10; i++) {
Sleep(1000);
printf(“B进程的Y线程 %d\n”, i);
}
ReleaseMutex(hMutex);
return 0;
}
事件的使用:
1、事件对象的创建
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全属性 NULL时为系统默认
BOOL bManualReset, // TRUE 通过调用ResetEvent将事件对象标记为未通知
BOOL bInitialState, // TRUE 已通知状态 FALSE未通知状态
LPCTSTR lpName // 对象名称 以NULL结尾的字符串
);
2、事件对象的控制
BOOL SetEvent(HANDLE hEvent); //将对象设置为已通知
3、关闭时间对象句柄
CloseHandle(); //关闭句柄
信号量的使用:
创建信号量
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG lInitialCount,
LONG lMaximumCount,
LPCTSTR lpName
);
函数说明:
第一个参数表示安全控制,一般直接传入NULL。
第二个参数表示初始资源数量。0时不发送信号
第三个参数表示最大并发数量。lInitialCount<=lMaximumCount
第四个参数表示信号量的名称,传入NULL表示匿名信号量。
打开信号量
HANDLE OpenSemaphore(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);
函数说明:
第一个参数表示访问权限,对一般传入SEMAPHORE_ALL_ACCESS。详细解释可以查看MSDN文档。
第二个参数表示信号量句柄继承性,一般传入FALSE即可。
第三个参数表示名称,不同进程中的各线程可以通过名称来确保它们访问同一个信号量。
递增信号量的当前资源计数
BOOL ReleaseSemaphore(
HANDLE hSemaphore,
LONG lReleaseCount,
LPLONG lpPreviousCount
);
函数说明:
第一个参数是信号量的句柄。
第二个参数表示增加个数,必须大于0且不超过最大资源数量。
第三个参数返回当前资源数量的原始值,设为NULL表示不需要传出。
注:没有一个函数可以用来查询信标的当前资源数量的值
信号量的清理与销毁
CloseHandle()