C#中使用事件机制实现线程间的通信
在图形界面中的运用程序中,通常是主线程负责UI人机交互,而将需要循环执行和耗资源的逻辑代码和复杂的业务实现以及数据交互放到单独一个线程里运行。这样可以保证主线程UI可以正常进行人机交互。
而线程之间的数据交互,就涉及了线程通信。这里将通过一个给MCU升级固件程序的IAP(在应用编程)的一个用C#写的上位机软件的实现原理,来说明如何通过事件的机制来实现线程之间的通信。
关于事件和委托的概念
事件(Event)用于进程间通信。
委托(Delegate)是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。
通过事件使用委托
事件在类中声明且生成,且通过使用同一个或其他类中的委托与事件处理程序关联。这被称为 发布器(publisher) 类。其他接受该事件的类被称为 订阅器(subscriber) 类。事件使用 发布-订阅(publisher-subscriber) 模型。
- 发布器(publisher) 是一个包含事件和委托定义的对象。事件和委托之间的联系也定义在这个对象中。发布器(publisher)类的对象调用这个事件,并通知其他的对象。
- 订阅器(subscriber) 是一个接受事件并提供事件处理程序的对象。在发布器(publisher)类中的委托调用订阅器(subscriber)类中的方法(事件处理程序)。
线程使用事件通信的基本流程
线程间基于事件机制通信的基本流程案例说明
正在更新.png在IAP上位机应用中,当用户通过窗体配置好各项参数,点击开始按钮后,便通过串口尝试和MCU进行通信,如果通信握手正确,便开始向MCU发送需要更新的固件数据,这时窗体程序需要显示下载的进度和结果。
在这个应用里,和MCU之间的串口通信是在子线程实现的,然而在UI应用中,子线程尝试更新还不属于自身线程的UI资源是不被允许的。
于是我便用了事件的机制,通过子线程将下载进度通过事件的形式发布出去,在主线程里订阅这个事件以及声明委托和定义事件的处理方法。在事件处理方法里获取下载进度,然后更新进度条。
主要的实现代码
声明事件
public event EventHandler NowDownloadProgressEvent;
在串口通信的线程类中声明当前下载进度事件
发布事件
int progress = (int)(((float)dataSize * packetNumber) / fileStream.Length * 100);
NowDownloadProgressEvent.Invoke(progress, new EventArgs());
当子线程开始与MCU通信,进行固件更新时,每循环发送完一个包,计算一次进度值,并将进度值以事件的形式发布出去。
事件订阅
downloadThread = new System.Threading.Thread(ymodem.YmodemUploadFile);
ymodem.NowDownloadProgressEvent += new EventHandler(NowDownloadProgressEvent);
ymodem.DownloadResultEvent += new EventHandler(DownloadFinishEvent);
主线程在启动子线程更新固件前,先订阅事件。这样子主线程就可以根据子线程发布的相关事件,进行一系列动作。
声明委托和定义事件处理方法
region 下载进度委托及事件响应
private delegate void NowDownloadProgress(int nowValue);
private void NowDownloadProgressEvent(objectsender,EventArgs e)
{
int value = Convert.ToInt32(sender);
NowDownloadProgress count = newNowDownloadProgress(UploadFileProgress);
this.Invoke(count, value);
}
private void UploadFileProgress(int count)
{
DownloadProgressBar.Value = count;
}
#endregion
主线程的类中,声明下载进度的委托和响应事件的方法。