Drunkmars's Blog

临界区与互斥体

字数统计: 1.7k阅读时长: 7 min
2021/07/16 Share

记录一下自己对临界区和互斥体的学习过程,主要是熟悉这两者的使用。

临界区

临界区指的是一个访问共用资源(例如:共用设备或是共用存储器)的程序片段,而这些共用资源又无法同时被多个线程访问的特性。当有线程进入临界区段时,其他线程或是进程必须等待(例如:bounded waiting 等待法),有一些同步的机制必须在临界区段的进入点与离开点实现,以确保这些共用资源是被互斥获得使用,例如:semaphore。只能被单一线程访问的设备,例如:打印机

调度法则

1、如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入。

2、任何时候,处于临界区内的进程不可多于一个。如已有进程进入自己的临界区,则其它所有试图进入临界区的进程必须等待。

3、进入临界区的进程要在有限时间内退出,以便其它进程能及时进入自己的临界区。

4、如果进程不能进入自己的临界区,则应让出CPU,避免进程出现“忙等”现象。

互斥体

在任意时刻,只有一个线程被允许进入代码保护区。任何线程在进入临界区之前,必须获取(acquire)与此区域相关联的互斥体的所有权。如果已有另一线程拥有了临界区的互斥体,其他线程就不能再进入其中。这些线程必须等待,直到当前的属主线程释放(release)该互斥体。什么时候需要使用互斥体呢?互斥体用于保护共享的易变代码,也就是,全局或静态数据。这样的数据必须通过互斥体进行保护,以防止它们在多个线程同时访问时损坏。

使用互斥不仅仅能够在同一应用程序不同线程中实现资源的安全共享,而且可以在不同应用程序的线程之间实现对资源的安全共享。

互斥体和临界区的区别

1、临界区只能用于单个进程间的线程控制

2、互斥体可以设定等待超时,但临界区不能

3、线程意外终结时,互斥体可以避免无限等待

4、互斥体效率没有临界区高

代码实现

要求:

第一步:在第一个文本框中输入一个值,比如1000;
第二步:点击按钮,同时创建3个线程,每个线程循环进行抢红包的操作,每次抢50
第三步:使用Mutex进行线程控制,当第一个文本框中的值<50时,抢红包线程结束

要求是用互斥体实现,这里我分别使用临界区和互斥体实现功能

利用临界区实现代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// win32 snatch.cpp : Defines the entry point for the application.
//

#include "stdafx.h"

HWND hSum, hEditA, hEditB, hEditC;
HANDLE hThreadArr[4], hMoniter;

CRITICAL_SECTION g_cs;

DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
::WaitForMultipleObjects(3, hThreadArr, TRUE, -1); //使用WaitForMultipleObjects监听线程

for (int i = 0; i < 3 ; i++)
{
::CloseHandle(hThreadArr[i]); //调用CloseHandle关闭句柄
}

MessageBox(0, TEXT("红包已抢完!"), TEXT("Snatch"), 0);

return 0;
}

DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
TCHAR szBuffer[10];
DWORD SumNum;
DWORD Num;

memset(szBuffer, 0 , sizeof(szBuffer));

while (true)
{
EnterCriticalSection(&g_cs); //进入临界区

GetWindowText(hSum, szBuffer ,10); //获取Sum窗口的数值
sscanf(szBuffer, "%d", &SumNum ); //字符串转整数


if (SumNum < 50)
{
LeaveCriticalSection(&g_cs); //若小于50则离开临界区
break;
}

SumNum -= 50;

//Sleep(200);

memset(szBuffer, 0 , sizeof(szBuffer)); //清空缓冲区
sprintf(szBuffer, "%d", SumNum); //整数转字符串
SetWindowText(hSum, szBuffer); //写回线程

//A B C窗口中的数值+50
memset(szBuffer, 0 ,sizeof(szBuffer)); //清空缓冲区

GetWindowText((HWND)lpParameter, szBuffer, 10); //获取子窗口的数值
sscanf(szBuffer, "%d", &Num); //字符串转整数

Num += 50;

memset(szBuffer, 0 ,sizeof(szBuffer)); //清空缓冲区
sprintf(szBuffer, "%d", Num); //整数转字符串
SetWindowText((HWND)lpParameter, szBuffer); //写回线程

LeaveCriticalSection(&g_cs); //离开临界区

Sleep(50);

}

return 0;
}



BOOL CALLBACK MainDlgProc(HWND hDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
BOOL bRet = FALSE;

switch(uMsg)
{
case WM_CLOSE:
{
EndDialog(hDlg,0);
break;
}
case WM_INITDIALOG:
{
hSum = GetDlgItem(hDlg,IDC_EDIT1);
hEditA = GetDlgItem(hDlg,IDC_EDIT2);
hEditB = GetDlgItem(hDlg,IDC_EDIT3);
hEditC = GetDlgItem(hDlg,IDC_EDIT4);


SetWindowText(hSum, "1000");
SetWindowText(hEditA, "0");
SetWindowText(hEditB, "0");
SetWindowText(hEditC, "0");

break;
}
case WM_COMMAND:

switch (LOWORD (wParam))
{
case IDC_BUTTON1:
{
//创建线程
hThreadArr[0] = ::CreateThread(NULL, 0, ThreadProc2, hEditA, 0, NULL);
hThreadArr[1] = ::CreateThread(NULL, 0, ThreadProc2, hEditB, 0, NULL);
hThreadArr[2] = ::CreateThread(NULL, 0, ThreadProc2, hEditC, 0, NULL);
hThreadArr[3] = ::CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL);

return TRUE;
}
}
break ;
}

return bRet;
}

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
InitializeCriticalSection(&g_cs);

DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG_MAIN),NULL,MainDlgProc);

return 0;
}

实现效果如下

test

互斥体实现代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// win32 snatch.cpp : Defines the entry point for the application.
//

#include "stdafx.h"

HWND hSum, hEditA, hEditB, hEditC;
HANDLE hThreadArr[4], hMoniter;

HANDLE hMutex;

DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
::WaitForMultipleObjects(3, hThreadArr, TRUE, -1); //使用WaitForMultipleObjects监听线程

for (int i = 0; i < 3 ; i++)
{
::CloseHandle(hThreadArr[i]); //调用CloseHandle关闭句柄
}

MessageBox(0, TEXT("红包已抢完!"), TEXT("Snatch"), 0);

return 0;
}

DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
TCHAR szBuffer[10];
DWORD SumNum;
DWORD Num;

memset(szBuffer, 0 , sizeof(szBuffer));

while (true)
{
WaitForSingleObject(hMutex, INFINITE);

GetWindowText(hSum, szBuffer ,10); //获取Sum窗口的数值
sscanf(szBuffer, "%d", &SumNum ); //字符串转整数


if (SumNum < 50)
{
ReleaseMutex(hMutex); //若小于50则离开临界区
break;
}

SumNum -= 50;

//Sleep(200);

memset(szBuffer, 0 , sizeof(szBuffer)); //清空缓冲区
sprintf(szBuffer, "%d", SumNum); //整数转字符串
SetWindowText(hSum, szBuffer); //写回线程

//A B C窗口中的数值+50
memset(szBuffer, 0 ,sizeof(szBuffer)); //清空缓冲区

GetWindowText((HWND)lpParameter, szBuffer, 10); //获取子窗口的数值
sscanf(szBuffer, "%d", &Num); //字符串转整数

Num += 50;

memset(szBuffer, 0 ,sizeof(szBuffer)); //清空缓冲区
sprintf(szBuffer, "%d", Num); //整数转字符串
SetWindowText((HWND)lpParameter, szBuffer); //写回线程

ReleaseMutex(hMutex); //离开临界区

Sleep(200);

}

OutputDebugStringF("%d", (DWORD)lpParameter);

return 0;
}



BOOL CALLBACK MainDlgProc(HWND hDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
BOOL bRet = FALSE;

switch(uMsg)
{
case WM_CLOSE:
{
EndDialog(hDlg,0);
break;
}
case WM_INITDIALOG:
{
hSum = GetDlgItem(hDlg,IDC_EDIT1);
hEditA = GetDlgItem(hDlg,IDC_EDIT2);
hEditB = GetDlgItem(hDlg,IDC_EDIT3);
hEditC = GetDlgItem(hDlg,IDC_EDIT4);


SetWindowText(hSum, "1000");
SetWindowText(hEditA, "0");
SetWindowText(hEditB, "0");
SetWindowText(hEditC, "0");

break;
}
case WM_COMMAND:

switch (LOWORD (wParam))
{
case IDC_BUTTON1:
{
//创建线程
hThreadArr[0] = ::CreateThread(NULL, 0, ThreadProc2, hEditA, 0, NULL);
hThreadArr[1] = ::CreateThread(NULL, 0, ThreadProc2, hEditB, 0, NULL);
hThreadArr[2] = ::CreateThread(NULL, 0, ThreadProc2, hEditC, 0, NULL);
hThreadArr[3] = ::CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL);

return TRUE;
}
}
break ;
}

return bRet;
}

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
hMutex = CreateMutex(NULL, false, TEXT("my mutex"));

DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG_MAIN),NULL,MainDlgProc);

return 0;
}

实现效果如下

test1

CATALOG
  1. 1. 临界区
    1. 1.1. 调度法则
  2. 2. 互斥体
  3. 3. 互斥体和临界区的区别
  4. 4. 代码实现