GUI应用程序(包括Windows窗体、WPF和Silverlight)引入一个线程处理模式。在这个模式中,创建窗口的线程是唯一能对那个窗口更新的线程。GUI中经常需要生成一个异步操作,使得GUI线程不至于堵塞并停止响应用户输入(鼠标、键盘等),然而异步完成后,完成该异步操作的的线程无法将结果更新至UI。因此,线程池线程必须采用某种方式让GUI线程更显UI。
有三种方式实现将结果同步到UI本文描述第一种方式;后两种方式及其对比放在下文中。
FCL定义了System.Threading.SynchronizationContext的基类,其中WinForm中派生出System.Windows.Forms.WindowsFormsSynchronizationContext;WPF和Silverlight派生出System.Windows.Threading.DispatcherSynchronizationContext子类。在这些类中需要使用以下三个函数
// 摘要:
// 获取当前线程的同步上下文。
//
// 返回结果:
// 一个 System.Threading.SynchronizationContext 对象,它表示当前同步上下文。
public static SynchronizationContext Current { get; }
//
// 摘要:
// 当在派生类中重写时,将异步消息调度到一个同步上下文。
//
// 参数:
// d:
// 要调用的 System.Threading.SendOrPostCallback 委托。
//
// state:
// 传递给委托的对象。
public virtual void Post(SendOrPostCallback d, object state);
//
// 摘要:
// 当在派生类中重写时,将一个同步消息调度到一个同步上下文。
//
// 参数:
// d:
// 要调用的 System.Threading.SendOrPostCallback 委托。
//
// state:
// 传递给委托的对象。
public virtual void Send(SendOrPostCallback d, object state);
其中SendOrPostCallback定义如下:
public delegate void SendOrPostCallback(object state);
以下代码描述了SynchronizationContext的使用方法:
仍以串口调用为例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Forms;
using System.IO;
using System.Threading;
namespace WindowsFormsApplication1
{
class MyWIndowsForm : Form
{
private System.IO.Ports.SerialPort serialPort1;
private RichTextBox richTextBox1;
private System.ComponentModel.IContainer components;
public MyWIndowsForm()
{
InitializeComponent();
serialPort1.Open();
Console.WriteLine("MainID:" + System.Threading.Thread.CurrentThread.GetHashCode());
}
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.serialPort1 = new System.IO.Ports.SerialPort(this.components);
this.richTextBox1 = new System.Windows.Forms.RichTextBox();
this.SuspendLayout();
//
// serialPort1
//
this.serialPort1.BaudRate = 4800;
this.serialPort1.ReadTimeout = 1000;
this.serialPort1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(this.serialPort1_DataReceived);
//
// richTextBox1
//
this.richTextBox1.Location = new System.Drawing.Point(12, 50);
this.richTextBox1.Name = "richTextBox1";
this.richTextBox1.Size = new System.Drawing.Size(690, 471);
this.richTextBox1.TabIndex = 0;
this.richTextBox1.Text = "";
//
// MyWIndowsForm
//
this.ClientSize = new System.Drawing.Size(714, 533);
this.Controls.Add(this.richTextBox1);
this.Name = "MyWIndowsForm";
this.Load += new System.EventHandler(this.MyWIndowsForm_Load);
this.ResumeLayout(false);
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
Console.WriteLine("\nEvent ID: " + System.Threading.Thread.CurrentThread.GetHashCode());
byte[] buffer = new byte[1024];
serialPort1.BaseStream.BeginRead(buffer, 0, 1024, SerialPortCallBack, buffer);
}
private static SynchronizationContext sc = null;
private void SerialPortCallBack(IAsyncResult result)
{
try
{
int count = serialPort1.BaseStream.EndRead(result);
byte[] data = (byte[])result.AsyncState;
//Console.WriteLine(result.AsyncWaitHandle.GetHashCode());
Console.WriteLine("\nCallBack ID: " + System.Threading.Thread.CurrentThread.GetHashCode());
for (int i = 0; i < count; i++)
{
Console.Write("{0:X2} ", data[i]);
if (sc == null)
{
richTextBox1.AppendText(string.Format("{0:X2} ", data[i]));
}
else
{
sc.Post(state =>
{
richTextBox1.AppendText(state.ToString());
}, string.Format("{0:X2} ", data[i]));
}
}
}
catch (IOException ex)
{
MessageBox.Show(ex.Message);
}
}
private void MyWIndowsForm_Load(object sender, EventArgs e)
{
sc = System.Threading.SynchronizationContext.Current;
}
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.Run(new MyWIndowsForm());
}
}
}
发送端图片:注意本次测试发送了47775个字节发送间隔为100ms
接收端:
Winform显示
Console显示:
其中为了提高效率将SynchronizationContext作为静态字段于各线程中共享使用。在Winform接受过程中可任意挪动窗口。完全实现了异步调用。