今天是圣诞节,大家 Merry Chrismas~
以前都是在C++项目中写界面,现在接触了C#感觉比MFC和QT好用多了,决定以后除了特殊要求外都用C#开发:)。记录一下用C#实现生产者消费者模式吧。
先介绍一下这个模式,简而言之就是生产者(可能有数个)生产东西,消费者(可能有数个)消费前面生产的东西。举个生活中的例子就是苹果有好几个厂家(生产者)生产iphone,线下线上的购买者(消费者)通过多种途径消耗掉iphone的库存。再举一个实际开发中的例子,我架设了四个摄像头同时不间断拍照,我需要不断的处理得到的图片,这也是生产者消费者模式。
这次演示用到的类文件还是以前用过的,产生随机数,链接如下:
https://blog.csdn.net/li3781695/article/details/84981827
先把界面弄的和这一样。
然后就可以开始了,直接上代码。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace Producer_Consumer_Test
{
public partial class Form1 : Form
{
bool NeedsStop = false;
bool NeedsStop1 = false;
bool NeedsStop2 = false;
bool NeedsStop3 = false;
bool NeedsStop4 = false;
Object thisLock = new Object();
List<int> RandomList = new List<int>();
public Form1()
{
InitializeComponent();
}
/***********************************************生产者按钮与消费者按钮**********************************************/
private void btn_Use_Start_Click(object sender, EventArgs e)
{
btn_Use_Start.Text = (btn_Use_Start.Text == "开始消费") ? "停止消费" : "开始消费";
if (btn_Use_Start.Text == "停止消费")
{
Thread Mythread1 = new Thread(Consumer1);
Mythread1.Start();
Thread Mythread2 = new Thread(Consumer2);
Mythread2.Start();
NeedsStop = false;
}
else NeedsStop = true;
}
private void btn_Producer1_Start_Click(object sender, EventArgs e)
{
btn_Producer1_Start.Text = (btn_Producer1_Start.Text == "开始生产") ? "停止生产" : "开始生产";
if (btn_Producer1_Start.Text == "停止生产")
{
Thread Mythread = new Thread(Thread1_Random);
Mythread.Start();
NeedsStop1 = false;
}
else NeedsStop1 = true;
}
private void btn_Producer2_Start_Click(object sender, EventArgs e)
{
btn_Producer2_Start.Text = (btn_Producer2_Start.Text == "开始生产") ? "停止生产" : "开始生产";
if (btn_Producer2_Start.Text == "停止生产")
{
Thread Mythread = new Thread(Thread2_Random);
Mythread.Start();
NeedsStop2 = false;
}
else NeedsStop2 = true;
}
private void btn_Producer3_Start_Click(object sender, EventArgs e)
{
btn_Producer3_Start.Text = (btn_Producer3_Start.Text == "开始生产") ? "停止生产" : "开始生产";
if (btn_Producer3_Start.Text == "停止生产")
{
Thread Mythread = new Thread(Thread3_Random);
Mythread.Start();
NeedsStop3 = false;
}
else NeedsStop3 = true;
}
private void btn_Producer4_Start_Click(object sender, EventArgs e)
{
btn_Producer4_Start.Text = (btn_Producer4_Start.Text == "开始生产") ? "停止生产" : "开始生产";
if (btn_Producer4_Start.Text == "停止生产")
{
Thread Mythread = new Thread(Thread4_Random);
Mythread.Start();
NeedsStop4 = false;
}
else NeedsStop4 = true;
}
/***********************************************线程函数**********************************************/
private void Thread1_Random()
{
MyRandom Ran = new MyRandom();
Ran.IsEven += new IsEvenEvent(SaveData);
while (true)
{
Ran.run_Random();
if (NeedsStop1 == true) break;
Thread.Sleep(500);
}
}
private void Thread2_Random()
{
MyRandom Ran = new MyRandom();
Ran.IsEven += new IsEvenEvent(SaveData);
while (true)
{
Ran.run_Random();
if (NeedsStop2 == true) break;
Thread.Sleep(500);
}
}
private void Thread3_Random()
{
MyRandom Ran = new MyRandom();
Ran.IsEven += new IsEvenEvent(SaveData);
while (true)
{
Ran.run_Random();
if (NeedsStop3 == true) break;
Thread.Sleep(500);
}
}
private void Thread4_Random()
{
MyRandom Ran = new MyRandom();
Ran.IsEven += new IsEvenEvent(SaveData);
while (true)
{
Ran.run_Random();
if (NeedsStop4== true) break;
Thread.Sleep(500);
}
}
//此函数将四个生产者线程产生的随机数压栈,此处用互斥锁锁住防止多线程同时访问
private void SaveData(object Sender, IsEvenEventArgs e)
{
lock (thisLock)
{
RandomList.Add(e.Random);
}
}
//消费者处理函数,数据处理区用锁锁住
private void Consumer1()
{
while (true)
{
if (NeedsStop == true) break;
Thread.Sleep(100);
lock (thisLock)
{
if (RandomList.Count == 0) continue;
if (RandomList.ElementAt(0) % 2 == 0)
{
textBox_Consumer1.Invoke
(
new Action
(
delegate
{
textBox_Consumer1.Text += RandomList.ElementAt(0).ToString()+"\r\n";
RandomList.RemoveAt(0);
textBox_Consumer1.Focus();//获取焦点
textBox_Consumer1.Select(this.textBox_Consumer1.TextLength, 0);//光标定位到文本最后
textBox_Consumer1.ScrollToCaret();//滚动到光标处
}
)
);
}
else RandomList.RemoveAt(0);
}
}
}
private void Consumer2()
{
while (true)
{
if (NeedsStop == true) break;
Thread.Sleep(100);
lock (thisLock)
{
if (RandomList.Count == 0) continue;
if (RandomList.ElementAt(0) % 2 == 0)
{
textBox_Consumer2.Invoke
(
new Action
(
delegate
{
textBox_Consumer2.Text += RandomList.ElementAt(0).ToString() + "\r\n";
RandomList.RemoveAt(0);
textBox_Consumer2.Focus();//获取焦点
textBox_Consumer2.Select(this.textBox_Consumer2.TextLength, 0);//光标定位到文本最后
textBox_Consumer2.ScrollToCaret();//滚动到光标处
}
)
);
}
else RandomList.RemoveAt(0);
}
}
}
}
}
先提一下生产随机数的类吧,自定义了一个事件,生成随机数的事件将偶数传回。
整个流程大致是:
1.按下开始消费按钮后,开启两个消费者线程,等待从链表头部得到数据,如得到就直接打印到文本框(两个消费者就开始如狼似虎的等待在apple专卖店门口,等待从多个工厂运过来的新款iphone)。
2.按下四个“开始生产”按钮以后,四个生产者不断触发随机数,得到的随机数将使用SaveData函数存入链表尾部(被生产出来的iphone就被两个消费者疯狂买买买,两个文本框就是这两个消费者的购物清单)。
3.此处涉及到此链表的地方都用互斥锁锁起来防止多线程同时访问同一块数据,这个模式也可以应用在图像处理上,这个也是一个用途十分广泛的模式。