windows多线程之读写同步(线程锁Mutex + 信号量Semaphore )

第一次使用博客,把自己学习的心得记录下来,与大家分享,有什么不足请指正,共同学习!
本文记录的是线程同步的一个经典问题,读写问题。这个场景在实际的应用中很常见,多线程中同时对文件进行读写很容易出问题,在此啰嗦一下,主要问题无外乎以下几种:
1.**丢失修改**。当A对文件进行修改后还没来得及更新,这时候B又对文件进行操作并将A的修改覆盖了,导致A实际保存的结果是B操作的结果;
2.**不可重复**: A对数据进行读取,在修改的过程中,B又对数据进行了读取并在A修改前进行了保存,当A再次读取文件进行结果验证的时候,发现啥都没做,结果就不对了。
其实就是对文件的操作没有保持原子性,这就需要通过互斥来保证同一时间只有一个线程能对文件进行操作;同时是谁先操作,谁继续操作(比如财务报销签字,先找部门领导,再找财务,顺序不对肯定签不了字,理论情况下),这就需要通过同步机制来保证。

 说了这么多概念,回到读写者问题。读者写者问题描述非常简单,有一个写者很多读者,多个读者可以同时读文件,但写者在写文件时不允许有读者在读文件,同样有读者在读文件时写者也不去能写文件。
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2e9e29b0bded455a8ccf7719a563b388.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAdzE1MTMxMjc3MTgw,size_12,color_FFFFFF,t_70,g_se,x_16#pic_center)



思路: 首先分析以上情况,基于以上的介绍,只要多线程操作文件的,肯定需要保持原子性,即互斥;其次肯定是有先后顺序的,即先向缓存内写入实际数据,当缓存区有了数据后才可以读取,即同步。
1.对于互斥,在Windows编程中,有很多种方法:互斥锁(Mutex),关键段(Critical Section等,我在这里用的是互斥锁Mutex;
2.对于同步。先分析问题种需要怎样的顺序。很简单,首先必须先写入缓存,当写入完毕的时候,通知读取的线程缓存区有内容了,缓存线程进行读取;缓存线程都读取完毕后,由**最后一个**读取的线程通知写入线程可以继续写入。
以上是问题的分析。
下面是我的实现过程,直接上代码,更多的是记录如何使用这些API:
#include "stdafx.h"
#include <Windows.h>
#include <iostream>


//-------------------------------------------读写同步问题----------------------------
/*

   一个用户写入和多个用户读取问题
   读取的时候不所有用户不可以写入
   写入的时候所有用户不可以读取

   思路: 写入的时候先等待写入信号量(初始化时可以先写入的,即信号量有写入资源数);
         设置一个已读取用户的个数变量,每一个读取的用户读取前先和总户数作比较,确认自己是否是最后一个读取用户
		 最后一个结束读文件的读者线程要负责通知读取线程可以继续写入。

  总结 : 1.写入的时候先等待写入信号,写入完释放读取信号
         2.读取的时候先读取写入信号,读取完再释放写入信号
		 3.同时,对数据操作的时候添加互斥锁
*/
extern HANDLE g_Mutex;                      //缓存区访问互斥锁
HANDLE g_ReadSemaphore, g_WriteSemaphore;   //读写信号量
HANDLE thread[4];                           //读写线程句柄
const unsigned int g_Size=3;                //总用户数
const unsigned int g_BufferSize=8;          //总的写入次数  
int buffer;                                 //读写的缓存区
int   g_Current_Read;                       //当前读取用户个数

unsigned int __stdcall writeThread(PVOID pM){

	for (int i = 0; i < g_BufferSize; i++){		
		WaitForSingleObject(g_WriteSemaphore, INFINITE);
		
		WaitForSingleObject(g_Mutex, INFINITE);
		buffer = i;
		printf("Writer is writting.. write buffer is:%d\n", buffer);
		ReleaseMutex(g_Mutex);

		ReleaseSemaphore(g_ReadSemaphore, g_Size, NULL);	
	}
	return 0;
}

unsigned int __stdcall readThread(PVOID pM){

	while (true){
		WaitForSingleObject(g_ReadSemaphore, INFINITE);
		
		WaitForSingleObject(g_Mutex, INFINITE);
		printf("Reader %d  is reading.. read buffer is:%d\n", g_Current_Read,buffer);
		g_Current_Read++;
		ReleaseMutex(g_Mutex);

		if (g_Current_Read == g_Size ){				
			ReleaseSemaphore(g_WriteSemaphore, 1, NULL);
			g_Current_Read = 0;
			//这里要注意:这里判断的是自己(读取线程)是不是最后一个读取的线程
			//如果是,那么就要向写入线程发送信号,告诉它可以继续写入
			//同时将g_Current_Read清零,即当前位置计数。
		}
	}
	return 0;
}

void useReadW(){
	g_ReadSemaphore = CreateSemaphore(NULL, 0, 3, NULL);
	//注意,CreateSemaphore的第2个参数是初始资源值,因为初始不能触发,必须保证写入线程发信号才能执行,所以这里置0
	//第3个参数是3,表示最大资源数为3,写入线程写入完毕后将资源加3,这三个读取线程可以读取缓存数据,因为已经实现了互斥,所以不会出问题
	//第4个参数是name,可以指定名称,当在多个进程种要实现线程的同步时,可以通过OpenSemaphore来打开此名字对应的信号锁。
	g_WriteSemaphore = CreateSemaphore(NULL, 1, 1, NULL);
	g_Mutex = CreateMutex(NULL, FALSE, NULL);
	//以上分别创建信号量、互斥锁
	//写入信号量g_WriteSemaphore初始资源为1(第2个参数),表示初始可以先写入,
	//其最大资源数也是1.这就侧面说明信号量也可以实现互斥,即信号量是一种特殊的互斥
	//这里的同步其实也可以用事件来实现,但是多个线程通知我觉得还是信号量方便一些

	thread[0] = (HANDLE)_beginthreadex(NULL, 0, writeThread, NULL, 0, NULL);
	thread[1] = (HANDLE)_beginthreadex(NULL, 0, readThread, NULL, 0, NULL);
	thread[2] = (HANDLE)_beginthreadex(NULL, 0, readThread, NULL, 0, NULL);
	thread[3] = (HANDLE)_beginthreadex(NULL, 0, readThread, NULL, 0, NULL);
  //初始同时启动读写线程,启动顺序没有要求,因为已经实现的同步
  //我查了以下,_beginthreadex内部其实调用的是CreateThread函数接口,
  //在此做了封转,保证了线程的安全性,建议用此方法。
	WaitForMultipleObjects(4, thread, TRUE, INFINITE);
	//此函数功能是让主线程阻塞,直到所有线程执行完毕后再继续
	//第1个参数是等待的线程数目
	//第2个参数的线程数组
	//第3个参数为true表示需要所有的线程都发出信号后才结束阻塞
	//第4个参数表示一直等待下去,不超时
	int i = 0;
	while (i < 4){
		CloseHandle(thread[i]);   //关闭读写线程句柄
	}
	CloseHandle(g_ReadSemaphore);
	CloseHandle(g_WriteSemaphore);  //关闭读写信号量句柄
	CloseHandle(g_Mutex);           //关闭互斥锁句柄	
}

以下是运行的结果:
运行结果

好了,今天就先记录到这里,有问题多谢指点!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值