编写C#窗体程序中如果遇到大量计算或者网络通信,UI很容易就僵死了,如下面的例子。
//阻塞5秒钟,窗体才有反应
public static int calc_res() {
Thread.Sleep(5000);
return 5;
}
private void calc_Click(object sender, EventArgs e)
{
int res = calc_res();
res_lbl.Text = res.ToString();
}
一个简单的解决方案就是使用线程方式运行大量计算,不过需要在计算完毕后Invoke回调主线程重绘控件。
private void calc_Click(object sender, EventArgs e)
{
Thread t = new Thread(
() => {
int res = calc_res();
Invoke( new Action(
() => {
res_lbl.Text = res.ToString();
}
));
}
);
t.Start();
}
线程方式开销较大,还可以考虑用异步回调的方式实现如下。
private void calc_Click(object sender, EventArgs e)
{
Func<int> t = calc_res;
t.BeginInvoke(new AsyncCallback(
(arg) =>
{
AsyncResult res = (AsyncResult)arg;
Func<int> _t = (Func<int>)res.AsyncDelegate;
string _s = _t.EndInvoke(arg).ToString();
this.Invoke(
new Action(
() => {
res_lbl.Text = _s;
}
)
);
}
), null);
}
上面的代码看起来比较复杂,可以用delegate使得代码逻辑更清晰,如下所示:
public delegate int cal_dele();
public void update_res(IAsyncResult ar) {
if (ar.IsCompleted) {
cal_dele cd = (cal_dele)((AsyncResult)ar).AsyncDelegate;
res_lbl.Text = cd.EndInvoke(ar).ToString();
}
}
private void calc_Click(object sender, EventArgs e)
{
cal_dele cd = calc_res;
IAsyncResult ar = cd.BeginInvoke(update_res, null);
//res_lbl.Text = cd.EndInvoke(ar).ToString(); //放在此处会阻塞
}
还可以利用form窗体的backgroundworker实现异步回调,它的底层实现也是异步方式。
private void calc_Click(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (sender1, e1) =>
{
e1.Result = calc_res();
};
worker.RunWorkerCompleted += (sender1, e1) =>
{
res_lbl.Text = e1.Result.ToString();
};
worker.RunWorkerAsync();
}
使用AsyncResult需要using System.Runtime.Remoting.Messaging,在更新到VS2000 sp1以后可以安装Async CTP,新增了async和await关键字,实现异步编程很容易。先添加引用AsyncCtpLibarary.dll,代码如下:
public static int calc_res() {
Thread.Sleep(5000);
return 5;
}
public Task<int> calc_res_async() {
return TaskEx.Run<int>(
() => calc_res()
);
}
private async void calc_Click(object sender, EventArgs e)
{
int res = await calc_res_async();
res_lbl.Text = res.ToString();
}
相关推荐: