一、关键段CS 和 互斥量Mutex 的相同点:都有线程拥有权

关键段和互斥量都有线程拥有权,即可以被一个线程拥有。在 前面讲关键段CS的文章中有说到,关键段结构体的第四个参数保存着拥有该关键段的线程的句柄,具体如下:


typedef struct _RTL_CRITICAL_SECTION {
PRTL_CRITICAL_SECTION_DEBUG DebugInfo; //
// The following three fields control entering and exiting the critical
// section for the resource
// LONG LockCount;
LONG RecursionCount;
HANDLE OwningThread; // from the thread's ClientId->UniqueThread
HANDLE LockSemaphore;
ULONG_PTR SpinCount; // force size on 64-bit systems when packed
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;
  • 第一个参数:PRTL_CRITICAL_SECTION_DEBUG DebugInfo; 调试的时候用的,先不做介绍。

  • 第二个参数:LONG LockCount; 初始化为-1,n表示有n个线程在等待。

  • 第三个参数:LONG RecursionCount; 表示该关键段的拥有线程对此资源获得关键段次数,初为0。

  • 第四个参数:HANDLE OwningThread; 即拥有该关键段的线程句柄

  • 第五个参数:HANDLE LockSemaphore; 实际上是一个自复位事件。

  • 第六个参数:ULONG_PTR SpinCount; 旋转锁的设置,用于多处理器。

现在我们来分析以下程序:


#include<iostream>
#include <windows.h>
using namespace std; const unsigned int THREAD_NUM = 10;
unsigned int g_Count = 0;
CRITICAL_SECTION cs;
DWORD WINAPI ThreadFunc(LPVOID); int main()
{
InitializeCriticalSection(&cs);
HANDLE hThread[THREAD_NUM];
for (int i = 0; i < THREAD_NUM; i++)
{
EnterCriticalSection(&cs); // 进入关键段,执行这一句时主线程就获得了这个关键段的拥有权。
hThread[i] = CreateThread(NULL, 0, ThreadFunc,0, 0, NULL);
}
WaitForMultipleObjects(THREAD_NUM, hThread,true,INFINITE);
cout << THREAD_NUM << " 个线程全部返回" << endl; return 0;
} DWORD WINAPI ThreadFunc(LPVOID p)
{
LeaveCriticalSection(&cs); // 离开关键段
Sleep(50);
EnterCriticalSection(&cs); // 进入关键段
cout<<"g_Count 的值为:"<<g_Count++<<endl;
LeaveCriticalSection(&cs); // 离开关键段
Sleep(50); return 0;
}

如下图所示加上两个断点进行调试,正常来说程序应该是依次经过两个断点,但是调试时我们发现,程序会多次重复进入第一个断点,这是因为执行到第一个断点式时主线程就获得了这个关键段的拥有权。

windows多线程(六) 互斥量Mutex与关键段CriticalSection比较-LMLPHP

同样地,Mutex也拥有线程所有权,需要了解互斥量看这里。和上面一样,我们写这样一个程序


#include <iostream>
#include <windows.h>
using namespace std; const unsigned int THREAD_NUM = 10;
unsigned int g_Count = 0;
CRITICAL_SECTION cs;
HANDLE g_Mutex;
DWORD WINAPI ThreadFunc(LPVOID); int main()
{
InitializeCriticalSection(&cs);
g_Mutex = CreateMutex(NULL, false, NULL); //初始化互斥量为触发状态
HANDLE hTread[THREAD_NUM];
for (int i = 0; i < THREAD_NUM;i++)
{
WaitForSingleObject(g_Mutex, INFINITE); //等待互斥量触发
hTread[i] = CreateThread(NULL, 0, ThreadFunc, 0, 0, NULL);
}
WaitForMultipleObjects(THREAD_NUM, hTread, true, INFINITE);
cout << THREAD_NUM << " 个线程全部返回" << endl;
return 0;
} DWORD WINAPI ThreadFunc(LPVOID p)
{
//ReleaseMutex(g_Mutex);
Sleep(50);
EnterCriticalSection(&cs); // 进入关键段
cout << "g_Count 的值为:" << g_Count++ << endl;
LeaveCriticalSection(&cs); // 离开关键段
Sleep(50);
ReleaseMutex(g_Mutex); //触发互斥量
return 0;
}

同样地,我们在程序中下两个断点,同样地程序会不经过第二个断点,而重复经过第一个断点。

windows多线程(六) 互斥量Mutex与关键段CriticalSection比较-LMLPHP

前面关键段和互斥量两篇文章我们说了关键段CS和互斥量Mutex不能做到线程同步,只能做到临界资源互斥访问,就是因为,他它们都有线程拥有权的原因。

二、关键段CS 和 互斥量Mutex 的不同点:由于互斥量常用于多进程之间的线程互斥,所以它比关键段还多一个很有用的特性——“遗弃”情况的处理。

看下面的程序:

程序一:


#include <stdio.h>
#include <windows.h> const char MutexName[] = "MyMutex"; //互斥量名字 int main()
{
HANDLE hMutex = CreateMutex(NULL, TRUE, MutexName); //创建互斥量并初始化为未触发状态
printf("互斥量已经创建,按任意键触发\n");
getch();
exit(0); //在互斥量触发前退出程序。
//ReleaseMutex(hMutex); // 触发互斥量
printf("互斥量已经被触发\n");
CloseHandle(hMutex);
return 0;
}

程序二:


#include <stdio.h>
#include <windows.h> const char MutexName[] = "MyMutex"; //互斥量名字 int main()
{
HANDLE hMutex = OpenMutex(MUTEX_ALL_ACCESS, TRUE, MutexName); //打开互斥量 if (NULL != hMutex)
{
printf("打开互斥量成功,等待互斥量被触发\n");
DWORD mRes = WaitForSingleObject(hMutex, INFINITE); // 等待互斥量被触发
if (WAIT_ABANDONED == mRes) //判断互斥量是否被遗弃
{
printf("互斥量被遗弃。\n");
}
//printf("互斥量已经被触发\n");
}
else
{
printf("互斥量打开失败。\n");
} CloseHandle(hMutex);
return 0;
}

先运行,程序一,然后运行程序二,如下图所示。

windows多线程(六) 互斥量Mutex与关键段CriticalSection比较-LMLPHP

此时在,程序一中按任意键,使程序一在互斥量未触发之前退出,程序二输出如下:

windows多线程(六) 互斥量Mutex与关键段CriticalSection比较-LMLPHP

这篇是边学边写出来的可能有不正确的地方,欢迎指出!!!!!

04-21 04:05