关键段与互斥量的陷阱

时间:2021-08-26 04:34:17

  在下有理解不到位,或是有更好的建议,欢迎批评指正!

  相同点:关键段和互斥量都可以用来控制线程互斥访问资源。

  不同点:关键段只能用于单进程间的多线程互斥,而互斥量可以用于多进程间的多线程互斥,而且互斥量可以处理“遗弃”(即某个个进程的某个线程占用了互斥量,但是它因为某些原因非正常关闭了,互斥量也没有释放,这是系统就是检测,处理这种情况,释放互斥量,以免其他线程一直等待下去)的问题。

    

  按这样理解的话,在单个进程内使用关键段与互斥量应该可以达到类似的结果,真的是这样子吗?

  个人用比较常见的生成者消费者模型稍作变型,成了4个生产者,4个消费者,4个临界区资源:

  用信号量full,empty表示临界区的资源,用关键段处理对变量的互斥访问。

#include<stdio.h>
#include
<process.h>
#include
<windows.h>

volatile long g_nLoginCount;
const int THREAD_NUM = 10;
volatile long g_num;

HANDLE g_Mutex;


HANDLE g_Semaphore_full,g_Semaphore_empty;
//信号量

CRITICAL_SECTION g_thread;

int num=10;

unsigned
int __stdcall producer(void *pPM){

int i;
for(i=0;i<4;i++){
Sleep(
100);
WaitForSingleObject(g_Semaphore_empty,INFINITE);
Sleep(
100);

EnterCriticalSection(
&g_thread); //用关键段不会有问题
// WaitForSingleObject(g_Mutex,INFINITE); //用互斥量就会出问题
g_num++;
printf(
"生产者ID:%d 累积SUM : %d\n",GetCurrentThreadId(),g_num);
// ReleaseMutex(&g_Mutex);
LeaveCriticalSection(&g_thread);

ReleaseSemaphore(g_Semaphore_full,
1,NULL);//信号量++
Sleep(100);
}
return 0;
}

unsigned
int __stdcall customer(void *pPM){
int ok=1;
while(1){
//Sleep(0);
Sleep(100);
Sleep(
0);
WaitForSingleObject(g_Semaphore_full,INFINITE);

printf(
" 消费者ID:%d 累积SUM : %d\n",GetCurrentThreadId(),g_num);

ReleaseSemaphore(g_Semaphore_empty,
1,NULL);//信号量++
}
return 0;

}

int main(){

g_Semaphore_full
= CreateSemaphore(NULL,0,4,NULL);//当前0个资源,最大允许4个同时访
g_Semaphore_empty = CreateSemaphore(NULL,0,4,NULL);//当前0个资源,最大允许4个同时访
g_Mutex = CreateMutex(NULL,FALSE,NULL);
InitializeCriticalSection(
&g_thread);

HANDLE handle[
10];

ReleaseSemaphore(g_Semaphore_empty,
4,NULL);//信号量++


int i;

for(i=0;i<4;i++){
handle[i]
= (HANDLE)_beginthreadex(NULL,0,producer,NULL,0,NULL);
}
for(i=0;i<4;i++){
handle[i
+4] = (HANDLE)_beginthreadex(NULL,0,customer,NULL,0,NULL);
}

WaitForMultipleObjects(THREAD_NUM,handle,TRUE,INFINITE);

getchar();
//一定要在这里设置使主线程停止,否则执行到后面的话,子线程就被关闭了

for(i=0;i<6;i++)
CloseHandle(handle[i]);
CloseHandle(g_Semaphore_full);
CloseHandle(g_Semaphore_empty);
CloseHandle(g_Mutex);

return 0;
}

关键段与互斥量的陷阱

这样是正常的。

但是用互斥量处理对g_num变量的互斥访问的时候:

#include<stdio.h>
#include
<process.h>
#include
<windows.h>

volatile long g_nLoginCount;
const int THREAD_NUM = 10;
volatile long g_num;

HANDLE g_Mutex;


HANDLE g_Semaphore_full,g_Semaphore_empty;
//信号量

CRITICAL_SECTION g_thread;

int num=10;

unsigned
int __stdcall producer(void *pPM){

int i;
for(i=0;i<4;i++){
Sleep(
100);
WaitForSingleObject(g_Semaphore_empty,INFINITE);
Sleep(
100);

// EnterCriticalSection(&g_thread); //用关键段不会有问题
WaitForSingleObject(g_Mutex,INFINITE); //用互斥量就会出问题
g_num++;
printf(
"生产者ID:%d 累积SUM : %d\n",GetCurrentThreadId(),g_num);
ReleaseMutex(
&g_Mutex);
// LeaveCriticalSection(&g_thread);

ReleaseSemaphore(g_Semaphore_full,
1,NULL);//信号量++
Sleep(100);
}
return 0;
}

unsigned
int __stdcall customer(void *pPM){
int ok=1;
while(1){
//Sleep(0);
Sleep(100);
Sleep(
0);
WaitForSingleObject(g_Semaphore_full,INFINITE);

printf(
" 消费者ID:%d 累积SUM : %d\n",GetCurrentThreadId(),g_num);

ReleaseSemaphore(g_Semaphore_empty,
1,NULL);//信号量++
}
return 0;

}

int main(){

g_Semaphore_full
= CreateSemaphore(NULL,0,4,NULL);//当前0个资源,最大允许4个同时访
g_Semaphore_empty = CreateSemaphore(NULL,0,4,NULL);//当前0个资源,最大允许4个同时访
g_Mutex = CreateMutex(NULL,FALSE,NULL);
InitializeCriticalSection(
&g_thread);

HANDLE handle[
10];

ReleaseSemaphore(g_Semaphore_empty,
4,NULL);//信号量++


int i;

for(i=0;i<4;i++){
handle[i]
= (HANDLE)_beginthreadex(NULL,0,producer,NULL,0,NULL);
}
for(i=0;i<4;i++){
handle[i
+4] = (HANDLE)_beginthreadex(NULL,0,customer,NULL,0,NULL);
}

WaitForMultipleObjects(THREAD_NUM,handle,TRUE,INFINITE);

getchar();
//一定要在这里设置使主线程停止,否则执行到后面的话,子线程就被关闭了

for(i=0;i<6;i++)
CloseHandle(handle[i]);
CloseHandle(g_Semaphore_full);
CloseHandle(g_Semaphore_empty);
CloseHandle(g_Mutex);

return 0;
}

关键段与互斥量的陷阱

观察生产者的ID:成了有序的情况,这就是说,它使得一个线程完全执行完了之后在执行另外一个线程,这跟关键段的作用也差的太大了!!到底是什么情况??