最简单生产消费者模型理解(线程同步,共享资源,信号量)
主要为了记忆,大牛或者小牛可以略过,主要基础的说明生产消费模型。
前言
生产消费者模型主要不生产者数据获取者和消费者数据处理者进行分离,达到解耦合作用,本文以夸父喝水为例,夸父和水可理解为消费者,而夸父的仆人可以理解为生产者,他们是大自然的搬运工,负者搬运水的。夸父是个老爷先让相关部分(计算机)准备一个喝水的器具缸(生产者和消费者共享资源),然后让仆人去搬运水(生产线程),如果仆人成功取水,则会要一下铃铛(信号量),而老爷夸父就会看缸里有没有水,如果有水他就喝水,喝完大喊一声某某水真好喝(写文件),如果没有水它就会等待,等待仆人给他摇铃,喝完水,没收夸父喝水相关器具,包括铃铛(回收资源)
程序变量
private object locker = new object();//同步锁
private Queue<string> waterCollection = new Queue<string>();//夸父喝水的缸,这里模拟,没有先进先出,毕竟水嘛
private Thread threadDrink = null;
private readonly AutoResetEvent ringEvent = new AutoResetEvent(false);//这个是个铃铛,一旦缸里有水就通知夸父来喝(夸父确实能够不是小狗么)
private readonly string path = @"C:\Users\anyonewho\Desktop\test.txt";
生产者
/// <summary>
/// 夸父仆人调水
/// </summary>
/// <param name="palceName"></param>
public void GetWaterByFileld(string palceName)
{
lock (locker)
{
waterCollection.Enqueue(palceName);
}
ringEvent.Set();//取到水后摇一摇铃铛
}
Enqueue方法是向队列中添加数据到尾部,即模拟仆人取到水,之后就可以摇铃铛同时夸父过来喝水啦,这个画面有点美,不怎么敢想(摇着尾巴蹬蹬的过来了…)
消费者
/// <summary>
/// 喝水事件
/// </summary>
public void DrinkWaterEvent()
{
string result = null;//喝水结果
while (true)
{
lock (locker)
{
if (waterCollection.Count > 0)//水缸中有水
{
result = waterCollection.Dequeue();
if (result == null) return;
result = result + "水真好喝啊\r\n";
}
}
if (result != null)
{
CryOut(result);//夸父取到水,他会大喊一声某某水真好喝
}
else
{
ringEvent.WaitOne();//打水的佣人还没取到水,只能等等了,等待铃铛响(set信号)
}
}
}
夸父喝水时候把水缸搬到自己面前免得有人跟他抢(锁住水缸),如果水缸中有水那么开始喝水(Dequeue从队列中取出队列头数据,同时队列会删除该数据,例如栈的Pop),喝完水会大喊一声某某水真好喝,这个我们写入到文件中了,如果取出水为null,也就是没水,那么夸父就坐在那边等待(WaitOne),如果读出数据长度且为null,这个是什么鬼?这个就是估计在队列中加入null值,告诉夸父水都被你喝完了,可以停下来了,即退出了。
写文件
/// <summary>
/// 喝到水后调用喊的技能
/// </summary>
/// <param name="result"></param>
public void CryOut(string result)
{
//这边cryOut动作我们写到文件中
try
{
if (!File.Exists(path)) File.Create(path);//文件存在做简单判断
lock (this)
{
using (FileStream stream = new FileStream(path, FileMode.Append))//以追加方式写文件
{
byte[] content = Encoding.UTF8.GetBytes(result);
stream.Write(content,0,content.Length);
}
}
Thread.Sleep(200);
}
catch(Exception error)
{
throw new Exception("调用喊技能发生异常!" + error.Message);
}
}
这边相对简单,就不扯了。
回收资源
public void DisPose()
{
lock (locker)
{
waterCollection.Enqueue(null);//通知水喝完了,该回家吃饭了
}
ringEvent.Set();
threadDrink.Join();
ringEvent.Close();
}
这里就是通知喝水结束,同时回收喝水相关的餐具,即铃铛,那么对于夸父喝水的缸呢,相关部分自己回收了(GC)。