C#窗体多线程实现生产者消费者模型(通过回调函数修改窗体线程控件)

您所在的位置:网站首页 C多线程生产者与消费者模型 C#窗体多线程实现生产者消费者模型(通过回调函数修改窗体线程控件)

C#窗体多线程实现生产者消费者模型(通过回调函数修改窗体线程控件)

2023-09-19 01:28| 来源: 网络整理| 查看: 265

C#窗体多线程实现生产者消费者模型(通过回调函数修改窗体线程控件)

目录 C#窗体多线程实现生产者消费者模型(通过回调函数修改窗体线程控件)一、运行结果二、关键代码详解2.1变量2.2开始运行2.3生产者线程的代码2.4消费者线程的代码2.5回调函数部分2.6更新boxes和信息栏2.7停止运行 三、所有代码展示       本文介绍C#窗体多线程的运用。工作线程由于不和窗体线程共享资源故无法直接修改窗体控件,故可使用回调函数通知窗体线程做出相应修改。       通过实现生产者消费者(使用信号量)的例子来阐述多线程回调函数的运用。

一、运行结果

图1

图1-1 图2

图1-2       本程序窗体分为三个模块如图1-1。左上角为初始化模块,填入生产者数量,消费者数量后可以点击begin开始运行,也可以点击stop结束运行;左下角为形象显示模块,有五个boxes代表buffer的容量,未装填时为空,装填后为全黑,该模块左下角为提示信息;右侧为运行具体细节展示,如图1-2. 二、关键代码详解 2.1变量

      信号量filled_num,empty_num分别代表buffer中占用资源数量和空闲资源数量,mutex控制互斥访问资源,producers,consumers数组分别存储生产者消费者进程。两个sleep变量代表各自的休眠时间。

Semaphore filled_num; Semaphore empty_num; Mutex mutex; Thread[] producers; Thread[] consumers; //一个生产者生产一个产品的用时,消费者同 const int ProduceSleep = 1000; const int ConsumeSleep = 500; //buffer容量 const int SemaphoreNum = 5; bool isStart; 2.2开始运行

      若未开始,则根据输入情况为生产者消费者创建相应数量的线程(用生产者的代码produce和消费者的代码consume,都有一个输入i作为线程代号),线程都设置为后台线程并且开始执行。

//当生产消费未开始时,创建semaphore和生产消费者的进程 private void SetNum(object sender, RoutedEventArgs e) { if(!isStart) { try { filled_num = new Semaphore(0, SemaphoreNum); empty_num = new Semaphore(SemaphoreNum, SemaphoreNum); Show.AppendText(""); consumers = new Thread[int.Parse(ConsumerNum.Text.Trim())]; producers = new Thread[int.Parse(ProducerNum.Text.Trim())]; } catch(Exception) { ShowInfo.Content = "Wrong entering, please correct."; return; } mutex = new Mutex(); isStart = true; for (int i = 0; i produce(i); }); producers[i].IsBackground = true; producers[i].Start(); } //创建生产者消费者线程 for (int i = 0; i consume(i); }); consumers[i].IsBackground = true; consumers[i].Start(); } ShowInfo.Content = "Running."; } else { ShowInfo.Content = "You have to stop first."; } } 2.3生产者线程的代码

      循环执行代码,若通过empty_num.waitone函数判断信号量empty_num是否有空闲,若有则进入生产者的休眠时间,休眠结束用mutex.waitone查看buffer是否空闲,若空闲调用update(ID)(update中进行了回调以通知主线程更新界面),更新后释放mutex并release一个filled_num信号量。

//生产者的produce,一秒钟生产一个,涂黑一个 public void produce(int ID) { while (true) { try { empty_num.WaitOne(); Thread.Sleep(ProduceSleep); mutex.WaitOne(); update(ID); mutex.ReleaseMutex(); filled_num.Release(); } catch (Exception) { return; } } } 2.4消费者线程的代码

      与生产者线程的相似,只不过是先信号量filled_num.waitone,最后释放的是empty_num。输入update的参数为负数代表是消费者进程。

//消费者的consume, 500毫秒消费一个,涂灰一个 public void consume(int ID) { while(true) { try { filled_num.WaitOne(); Thread.Sleep(ConsumeSleep); mutex.WaitOne(); update(-1 * ID); mutex.ReleaseMutex(); empty_num.Release(); } catch(Exception) { return; } } } 2.5回调函数部分

      ID为负数则为消费者,正数为生产者。先通过窗体的storepanel(也就是显示buffer的五个box所在的panel)和Show(也就是右侧的信息显示文本框)的Dispatcher查看是否可回调(其底层实现原理也是消息队列),若都可以,则通过Update委托调用UpdateBoxes(更新boxes)和UpdateShow(更新右侧信息栏)。

//生产者消费者的回调函数 public void update(int ID) { //无法访问则使用回调函数 if (!Storepanel.Dispatcher.CheckAccess()&&!Show.Dispatcher.CheckAccess()) { //声明,并实例化回调 Update d = UpdateBoxes; //使用回调 Storepanel.Dispatcher.Invoke(d, ID); Update f = UpdateShow; Show.Dispatcher.Invoke(f, ID); } } 2.6更新boxes和信息栏

      UpdateBox若是消费者调用则id为负,若是生产者则为正。若是生产者则正向遍历StorePanel中的boxes(实际上是label,通过改变背景色标示是否为空)寻找一个空闲label改变其backgroud值,消费者亦然。       UpdateShow 更新窗体中的信息,采取方式与UpdateBox相似。       这两个函数都通过委托使得生产者消费者进程能够回调更新窗体。

private delegate void Update(int index); //更新窗体中的storestack,index绝对值为生产者或消费者的编号,负的是消费者 public void UpdateBoxes(int ID) { if(ID Label temp = Storepanel.Children[i] as Label; if (temp.Background != Brushes.Gray) { temp.Background = Brushes.Gray; break; } } } else {//生产者 for (int i = 0; i temp.Background = Brushes.Black; break; } } } } //更新窗体中Show的信息 public void UpdateShow(int ID) { if (ID Show.AppendText("Producer" + ID + " produces an item.\n"); } Show.ScrollToEnd(); } 2.7停止运行

      通过原先记录的线程数组,遍历线程用Abort函数释放。

private void StopButton_Click(object sender, RoutedEventArgs e) { if (isStart) { //abort后台线程 for (int i = 0; i consumers[i].Abort(); } //复原boxes for (int i = 0; i ShowInfo.Content = "You have to start first."; } } 三、所有代码展示

        代码中由于为了调试方便和运行流畅使用了较多的try-catch,但catch之后未作处理,读者可自行完善。

using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Threading; namespace ProduserAndConsumer { /// /// MainWindow.xaml 的交互逻辑 /// public partial class MainWindow : Window { Semaphore filled_num; Semaphore empty_num; Mutex mutex; Thread[] producers; Thread[] consumers; //一个生产者生产一个产品的用时,消费者同 const int ProduceSleep = 1000; const int ConsumeSleep = 500; //buffer容量 const int SemaphoreNum = 5; bool isStart; public MainWindow() { InitializeComponent(); this.Closing += Window_Closing; isStart = false; for (int i = 0; i if(!isStart) { try { filled_num = new Semaphore(0, SemaphoreNum); empty_num = new Semaphore(SemaphoreNum, SemaphoreNum); Show.AppendText(""); consumers = new Thread[int.Parse(ConsumerNum.Text.Trim())]; producers = new Thread[int.Parse(ProducerNum.Text.Trim())]; } catch(Exception) { ShowInfo.Content = "Wrong entering, please correct."; return; } mutex = new Mutex(); isStart = true; //创建生产者消费者线程 for (int i = 0; i produce(i); }); producers[i].IsBackground = true; producers[i].Start(); } for (int i = 0; i consume(i); }); consumers[i].IsBackground = true; consumers[i].Start(); } ShowInfo.Content = "Running."; } else { ShowInfo.Content = "You have to stop first."; } } //生产者的produce,一秒钟生产一个,涂黑一个 public void produce(int ID) { while (true) { try { empty_num.WaitOne(); Thread.Sleep(ProduceSleep); mutex.WaitOne(); update(ID); mutex.ReleaseMutex(); filled_num.Release(); } catch (Exception) { return; } } } //消费者的consume, 500毫秒消费一个,涂灰一个 public void consume(int ID) { while(true) { try { filled_num.WaitOne(); Thread.Sleep(ConsumeSleep); mutex.WaitOne(); update(-1 * ID); mutex.ReleaseMutex(); empty_num.Release(); } catch(Exception) { return; } } } //生产者消费者的回调函数 public void update(int ID) { //无法访问则使用回调函数 if (!Storepanel.Dispatcher.CheckAccess()&&!Show.Dispatcher.CheckAccess()) { //声明,并实例化回调 Update d = UpdateBoxes; //使用回调 Storepanel.Dispatcher.Invoke(d, ID); Update f = UpdateShow; Show.Dispatcher.Invoke(f, ID); } } private delegate void Update(int index); //更新窗体中的storestack,index绝对值为生产者或消费者的编号,负的是消费者 public void UpdateBoxes(int ID) { if(ID Label temp = Storepanel.Children[i] as Label; if (temp.Background != Brushes.Gray) { temp.Background = Brushes.Gray; break; } } } else {//生产者 for (int i = 0; i temp.Background = Brushes.Black; break; } } } } //更新窗体中Show的信息 public void UpdateShow(int ID) { if (ID Show.AppendText("Producer" + ID + " produces an item.\n"); } Show.ScrollToEnd(); } //窗口关闭则关闭所有线程 public void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { if (isStart) { for (int i = 0; i consumers[i].Abort(); } } } private void StopButton_Click(object sender, RoutedEventArgs e) { if (isStart) { //abort后台线程 for (int i = 0; i consumers[i].Abort(); } //复原boxes for (int i = 0; i ShowInfo.Content = "You have to start first."; } } } }

      由于本实验完成的时间较久远所以一些记录的参考文献找不着了,如有雷同,不胜荣幸(可以提醒我加上参考文献)。 文件下载链接: http://www.lolxun.com/resource/2249796.html



【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3