互斥量,事件对象,信号量对象,关键代码段(临界区)

互斥量互斥量又称互斥锁。

互斥量是一个可以处于两态之一的变量:解锁和加锁。
如果不需要信号量的计数能力,有时可以使用信号量的一个简化版本,称为互斥量(mutex)。
互斥量仅仅适用于管理共享资源或一小段代码。
由于互斥量在实现时既容易又有效,这使得互斥量在实现用户空间线程包时非常有 用。
为协调共同对一个共享资源的单独访问而设计的。

#include <stdio.h>
#include <windows.h>
#include <process.h>
#define NUM_THREAD 50
unsigned WINAPI threadInc(void* arg);
unsigned WINAPI threadDes(void* arg);
long long num = 0;
HANDLE hMutex;
int main(int argc, char* argv[])
{
	HANDLE tHandles[NUM_THREAD];
	int i;
	hMutex = CreateMutex(NULL, FALSE, NULL);//创建互斥对象
	for (i = 0; i < NUM_THREAD; i++)
	{
		if (i % 2)
			tHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadInc, NULL, 0, NULL);
		else
			tHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadDes, NULL, 0, NULL);
	}
	WaitForMultipleObjects(NUM_THREAD, tHandles, TRUE, INFINITE);
	CloseHandle(hMutex);
	printf("result: %lld \n", num);
	return 0;
}
unsigned WINAPI threadInc(void* arg)
{
	int i;
	WaitForSingleObject(hMutex, INFINITE);
	for (i = 0; i < 500000; i++)
		num += 1;
	ReleaseMutex(hMutex);
	return 0;
}
unsigned WINAPI threadDes(void* arg)
{
	int i;
	WaitForSingleObject(hMutex, INFINITE);
	for (i = 0; i < 500000; i++)
		num -= 1;
	ReleaseMutex(hMutex);
	return 0;
}

事件对象

事件的主要用途是标志事件的发生,并以此协调线程的执行顺序。
用来通知线程有一些事件已发生,从而启动后继续任务的开始。
事件对象也可以通过通知操作方式来保持线程的同步,并且可以实现不同进程中的线程同步操作。

#include <stdio.h>
#include <windows.h>
#include <process.h>
#define STR_LEN 100
unsigned WINAPI NumberOfA(void* arg);
unsigned WINAPI NumberOfOthers(void* arg);
static char str[STR_LEN];
static HANDLE hEvent;
int main(int argc, char* argv[])
{
	HANDLE hThread1, hThread2;
	fputs("Input string: ", stdout);
	fgets(str, STR_LEN, stdin);
	//NUll 默认的安全符 手动 FALSE 初始状态为无信号状态
	hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	hThread1 = (HANDLE)_beginthreadex(NULL, 0, NumberOfA, NULL, 0, NULL);
	hThread2 = (HANDLE)_beginthreadex(NULL, 0, NumberOfOthers, NULL, 0, NULL);
	WaitForSingleObject(hThread1, INFINITE);
	WaitForSingleObject(hThread2, INFINITE);
	//直到2个线程执行完之后,再把事件设置为无信号状态
	ResetEvent(hEvent);
	CloseHandle(hEvent);
	system("pause");
	return 0;
}
unsigned WINAPI NumberOfA(void* arg)
{
	int i, cnt = 0;
	//再没有执行fputs("Input string: ", stdout);
	//fgets(str, STR_LEN, stdin);SetEvent(hEvent);之前,卡在
	//WaitForSingleObject
	WaitForSingleObject(hEvent, INFINITE);
	for (i = 0; str[i] != 0; i++)
	{
		if (str[i] == 'A')
			cnt++;
	}
	printf("Num of A: %d \n", cnt);
	return 0;
}
unsigned WINAPI NumberOfOthers(void* arg)
{
	int i, cnt = 0;
	//再没有执行fputs("Input string: ", stdout);
	//fgets(str, STR_LEN, stdin);SetEvent(hEvent);之前,卡在
	//WaitForSingleObject
	// WaitForSingleObject(hEvent, INFINITE);
	for (i = 0; str[i] != 0; i++)
	{
		if (str[i] != 'A')
			cnt++;
	}
	printf("Num of others: %d \n", cnt - 1);
	//把事件对象设置为有信号状态
	SetEvent(hEvent);
	return 0;
}
//买票
#include <stdio.h>
#include <windows.h>
#include <process.h>
int iTickets = 100;
HANDLE g_hEvent;
DWORD WINAPI SellTicketA(void* lpParam)
{
	while (1)
	{
		WaitForSingleObject(g_hEvent, INFINITE);
		if (iTickets > 0)
		{
			Sleep(1);
			iTickets--;
			printf("A remain %d\n", iTickets);
		}
		else
		{
			break;
		}
		SetEvent(g_hEvent);
	}
	return 0;
}
DWORD WINAPI SellTicketB(void* lpParam)
{
	while (1)
	{
		WaitForSingleObject(g_hEvent, INFINITE);
		if (iTickets > 0)
		{
			Sleep(1);
			iTickets--;
			printf("B remain %d\n", iTickets);
		}
		else
		{
			break;
		}
		SetEvent(g_hEvent);
	}
	return 0;//0 内核对象被销毁
}
int main()
{
	HANDLE hThreadA, hThreadB;
	hThreadA = CreateThread(NULL, 0, SellTicketA, NULL, 0, 0);// 2
	hThreadB = CreateThread(NULL, 0, SellTicketB, NULL, 0, 0);
	CloseHandle(hThreadA); //1
	CloseHandle(hThreadB);
	//自动重置事件对象 第二个参数为FALSE
	g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	SetEvent(g_hEvent);
	Sleep(8000);
	CloseHandle(g_hEvent);
	system("pause");
	return 0;
}

信号量对象

主要是实现同步,可以跨进程) 信号量是一个内核对象,可用来管理大量有限的系统资源
一个使用计数
32位整数,最大资源数量
32位整数,当前资源数量

信号量使用规则:
当前资源数量大于0,则等待信号量的线程获得资源继续运行,当前资源数量 减1
当前资源数量等于0,则等待信号量的线程继续等待,直到有线程释放信号量, 使当前资源数量大于0

#include <stdio.h>
#include <windows.h>
#include <process.h>
unsigned WINAPI Read(void* arg);
unsigned WINAPI Accu(void* arg);
static HANDLE semOne;
static HANDLE semTwo;
static int num;
int main(int argc, char* argv[])
{
	HANDLE hThread1, hThread2;
	semOne = CreateSemaphore(NULL, 0, 1, NULL);
	//semOne 没有可用资源 只能表示0或者1的二进制信号量 无信号
	semTwo = CreateSemaphore(NULL, 1, 1, NULL);
	//semTwo 有可用资源,有信号状态 有信号
	hThread1 = (HANDLE)_beginthreadex(NULL, 0, Read, NULL, 0, NULL);
	hThread2 = (HANDLE)_beginthreadex(NULL, 0, Accu, NULL, 0, NULL);
	WaitForSingleObject(hThread1, INFINITE);
	WaitForSingleObject(hThread2, INFINITE);
	CloseHandle(semOne);
	CloseHandle(semTwo);
	system("pause");
	return 0;
}
unsigned WINAPI Read(void* arg)
{
	int i;
	for (i = 0; i < 5; i++)
	{
		fputs("Input num: ", stdout); 
		printf("begin read\n"); 
		//等待内核对象semTwo的信号,如果有信号,继续执行;如果没有信号,等待
		WaitForSingleObject(semTwo, INFINITE);
		printf("beginning read\n"); //4 10 16
		scanf("%d", &num);
		ReleaseSemaphore(semOne, 1, NULL);
	}
	return 0;
}
unsigned WINAPI Accu(void* arg)
{
	int sum = 0, i;
	for (i = 0; i < 5; i++)
	{
		printf("begin Accu\n"); 
		//等待内核对象semOne的信号,如果有信号,继续执行;如果没有信号,等待
		WaitForSingleObject(semOne, INFINITE);
		printf("beginning Accu\n"); 
		sum += num;
		printf("sum = %d \n", sum); 
		ReleaseSemaphore(semTwo, 1, NULL);
	}
	printf("Result: %d \n", sum);
	return 0;
}

关键代码段(临界区)

每个进程中访问临界资源的那段代码称为临界区(Critical Section)(临界 资源是一次仅允许一个进程使用的共享资源)。每次只准许一个进程进入临界区, 进入后不允许其他进程进入。不论是硬件临界资源,还是软件临界资源,多个进程 必须互斥地对它进行访问。多个进程中涉及到同一个临界资源的临界区称为相关临界区。

进程进入临界区的调度原则
如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入。任何时候,处于临界区内的进程不可多于一个。如已有进程进入自己的临界区, 则其它所有试图进入临界区的进程必须等待。进入临界区的进程要在有限时间内退出,以便其它进程能及时进入自己的临界区。如果进程不能进入自己的临界区,则应让出CPU,避免进程出现“忙等”现象。

//卖票系统
#include <stdio.h>
#include <windows.h>
#include <process.h>
int iTickets = 5000;
CRITICAL_SECTION g_cs;
// A窗口 B窗口
DWORD WINAPI SellTicketA(void* lpParam)
{
	while (1)
	{
		EnterCriticalSection(&g_cs);//进入临界区
		if (iTickets > 0)
		{
			Sleep(1);
			iTickets--;
			printf("A remain %d\n", iTickets);
			LeaveCriticalSection(&g_cs);//离开临界区
		}
		else
		{
			LeaveCriticalSection(&g_cs);//离开临界区
			break;
		}
	}
	return 0;
}
DWORD WINAPI SellTicketB(void* lpParam)
{
	while (1)
	{
		EnterCriticalSection(&g_cs);//进入临界区
		if (iTickets > 0)
		{
			Sleep(1);
			iTickets--;
			printf("B remain %d\n", iTickets);
			LeaveCriticalSection(&g_cs);//离开临界区
		}
		else
		{
			LeaveCriticalSection(&g_cs);//离开临界区
			break;
		}
	}
	return 0;
}
int main()
{
	HANDLE hThreadA, hThreadB;
	hThreadA = CreateThread(NULL, 0, SellTicketA, NULL, 0, NULL); //2
	hThreadB = CreateThread(NULL, 0, SellTicketB, NULL, 0, NULL); //2
	CloseHandle(hThreadA); //1
	CloseHandle(hThreadB); //1
	InitializeCriticalSection(&g_cs); //初始化关键代码段
	Sleep(40000);
	DeleteCriticalSection(&g_cs);//删除临界区
	system("pause");
	return 0;
}

比较

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值