多线程同步问题——关键段、互斥量、事件、信号量

自己对多线程编程同步方面,从新梳理,也算复习了。^_^

经典多线程同步问题:

程序描述:

主线程启动10个子线程并将表示子线程序号的变量地址作为参数传递给子线程。子线程接收参数 -> sleep(50) ->全局变量++ -> sleep(0) -> 输出参数和全局变量。

要求:

1.子线程输出的线程序号不能重复。

2.全局变量的输出必须递增。


首先拿到这个问题,看到全局变量的递增,就想到了关键代码段,互斥量,事件等方法。自己挑了个互斥量的方法来写,但在自己写完调试发现,要求1:子线程输出的序号不能重复,写了互斥量方法居然不成功。这就蛋疼了~~~贴上互斥量的代码

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

long g_lngNum;
const int THREAD_NUM = 10;
unsigned int _stdcall Fun(void* pPM);

HANDLE g_hMutex = NULL;

int main()
{
	g_lngNum = 0;
	HANDLE handle[THREAD_NUM];
	g_hMutex = CreateMutex(NULL, FALSE, L"Mutex");
	for (int i = 0; i < THREAD_NUM; ++i)
	{
		handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
		WaitForSingleObject(g_hMutex, INFINITE);
	}

	WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
	CloseHandle(g_hMutex);
	for (int i = 0; i < THREAD_NUM; i++)
	{
		CloseHandle(handle[i]);
	}
	return 0;
}

unsigned int _stdcall Fun( void* pPM )
{
	int nThreadNum = *(int*)pPM;
	ReleaseMutex(g_hMutex);
	Sleep(50);
	g_lngNum++;
	Sleep(0);
	printf("线程编号为%d  全局资源值为%d\n", nThreadNum, g_lngNum);
	return 0;
}

输出结果:

线程编号为10 全局资源值为1

线程编号为10 全局资源值为2

线程编号为10 全局资源值为3

.....

全局资源互斥的递增,但是线程编号会出现重复。。。。


在调试的时候发现,在for里面和子线程里面打断点 调试时候并不会交替执行,而是一直在for里面执行,这感到纳闷了

WaitForSingleObject等待到信号,会将该计数器置为1 下次再等待的时候g_hMutex肯定没信号啊,那for怎么能一直执行。

后面发现了,互斥量的一个特性:线程所有权。在主线程for循环创建线程时候,等待到了互斥量有信号,相当于主线程拿到了通行证,等到第二次for时候 在wait检测的时候,发现主线程有通行证 就放行了....

在网上查阅了相关资料后发现,关键段代码、互斥量都有线程所有权的概念,解决这个同步问题,可用事件和信号量来解决。。。。。。

事件的代码

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

long g_lngNum;
const int THREAD_NUM = 10;
unsigned int _stdcall Fun(void* pPM);

HANDLE g_hEvent = NULL;

int main()
{
	g_lngNum = 0;
	HANDLE handle[THREAD_NUM];
	g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	for (int i = 0; i < THREAD_NUM; ++i)
	{
		handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
		WaitForSingleObject(g_hEvent, INFINITE);
	}
	
	WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
	CloseHandle(g_hEvent);
	for (int i = 0; i < THREAD_NUM; i++)
	{
		CloseHandle(handle[i]);
	}
	return 0;
}

unsigned int _stdcall Fun( void* pPM )
{
	int nThreadNum = *(int*)pPM;
	SetEvent(g_hEvent);
	Sleep(50);
	g_lngNum++;
	printf("线程编号为%d  全局资源值为%d\n", nThreadNum, g_lngNum);
	return 0;
}

才用事件方式,经典线线程同步问题已经圆满的解决了——线程编号的输出没有重复,说明主线程与子线程达到了同步。全局资源的输出是递增的,说明各子线程已经互斥的访问和输出该全局资源。

顺便贴上关键段代码和信号量代码

关键段代码:

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

long g_lngNum;
const int THREAD_NUM = 10;
unsigned int _stdcall Fun(void* pPM);
CRITICAL_SECTION g_csThreadp, g_csThreadCode;

int main()
{
	g_lngNum = 0;
	HANDLE handle[THREAD_NUM];
	InitializeCriticalSection(&g_csThreadp);
	InitializeCriticalSection(&g_csThreadCode);

	for (int i = 0; i < THREAD_NUM; ++i)
	{
		EnterCriticalSection(&g_csThreadp);
		handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
	}

	WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
	for (int i = 0; i < THREAD_NUM; i++)
	{
		CloseHandle(handle[i]);
	}
	DeleteCriticalSection(&g_csThreadp);
	DeleteCriticalSection(&g_csThreadCode);
	return 0;
}

unsigned int _stdcall Fun( void* pPM )
{
	int nThreadNum = *(int*)pPM;
	LeaveCriticalSection(&g_csThreadp);
	Sleep(50);
	EnterCriticalSection(&g_csThreadCode);
	g_lngNum++;
	printf("线程编号为%d  全局资源值为%d\n", nThreadNum, g_lngNum);
	LeaveCriticalSection(&g_csThreadCode);
	return 0;
}

信号量代码

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

long g_lngNum;
const int THREAD_NUM = 10;
unsigned int _stdcall Fun(void* pPM);

HANDLE g_hThreadParameter;  

int main()
{
	g_lngNum = 0;
	HANDLE handle[THREAD_NUM];
	g_hThreadParameter = CreateSemaphore(NULL, 0, 1, NULL); //创建一个初始资源为0,最大并发数为1的信号量
	for (int i = 0; i < THREAD_NUM; ++i)
	{
		handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
		WaitForSingleObject(g_hThreadParameter, INFINITE); // 信号量资源大于0触发,触发将资源减一
	}

	WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
	CloseHandle(g_hThreadParameter);
	for (int i = 0; i < THREAD_NUM; i++)
	{
		CloseHandle(handle[i]);
	}
	return 0;
}

unsigned int _stdcall Fun( void* pPM )
{
	int nThreadNum = *(int*)pPM;
	ReleaseSemaphore(g_hThreadParameter, 1, NULL);	//信号量++
	Sleep(50);
	g_lngNum++;
	printf("线程编号为%d  全局资源值为%d\n", nThreadNum, g_lngNum);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值