在桌面應用中,我們希望當一個費時的運算在進行的時候,當前窗體可以有所表現,比如顯示等待動畫或者進度條,避免讓用戶陷入無聊、乏味又不知何時才能執行完成的苦悶之中,此時異步計算即可派上用場,所謂異步,即是將費時的運算放到一個專門的工作線程裡面去,不在當前UI線程裡面處理,如果在UI線程內處理,UI勢必會進入假死狀態,期間用戶無法移動窗體,無法取消費時操作,只能等待程序處理完畢,程序的控制權才會重新交換給用戶,這種方式前面也說了,相當的不友好!其實這裡的異步操作類似於web開發中常用的AJax,調用XMLHTTP在後台操作,等操作完成後調用回調函數,給用戶結果,控制權一直都在用戶手裡,感覺肯定不一樣。:)
好了,說了使用和不用異步運算的區別,我們下邊說說在.Net中,使用C#語言,實現異步操作的兩種方式:
一,使用delegate代理
1、假如需要在工作線程進行長時間工作的方法是:int StartExportData()
//導出舊庫
public int StartExportData()
{
return new ExportData().StartExportDataToTmp();
}
2、定義上述方法的委托方法
//StartExportData方法委托,異步調用StartExportData方法,避免主線程阻塞。
delegate int StartExportDataDelegate();
3、定義回調方法,指示當StartExportData()執行完成後所進行的操作
//回調方法,當StartExportData方法執行完畢返回後執行的回調。
public void ExportCallBackMethod(System.IAsyncResult exportResult)
{
//獲得委托對象
StartExportDataDelegate d1 = (StartExportDataDelegate)exportResult.AsyncState;
//EndInvoke方法必須調用,取得被委托方法的返回值
int result = d1.EndInvoke(exportResult);
if (result == 1) //如果返回為1,表示外部程序成功返回
{
ShowMessage("成功導出舊年度數據庫 ..."); //更新信息提示
}
d1 = null;
}
4、在新的工作線程內啟動被代理的方法
//初始化委托方法對象
StartExportDataDelegate d1 = new StartExportDataDelegate(StartExportData);
//初始化回調方法對象
System.AsyncCallback c1 = new System.AsyncCallback(ExportCallBackMethod);
//使用BeginInvoke方法啟動異步線程,在線程內執行被委托方法StartExportData,
//線程執行完畢後調用回調方法CallBackMethod
d1.BeginInvoke(c1, d1);
5、最後,要說明的是,需要引入Threading:
using System.Threading;
二、使用BackgroundWorker 類,它是在 .Net Framework 2.0 版中是新增的。
還是對上述方法的異步調用,換用BackgroundWorker後會是怎樣的呢?我們一起來看一下:
1、初始化類的兩個事件:DoWork和RunWorkerCompleted,顧名思義,DoWork即為工作線程,在該方法內調用工作方法; RunWorkerCompleted即為回調方法,即為工作方法完成後所調用的方法。這裡的bgWorker為BackgroundWorker類的實例。
/// <summary>
/// 初始化工作線程事件處理程序
/// </summary>
private void initBackgroundWorker()
{
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
}
2、在類的構造函數內調用上述初始化方法。
public BaseDataForm()
{
InitializeComponent();
initBackgroundWorker();
}
3、修改方法的簽名,增加兩個參數。
private int StartExportData(BackgroundWorker worker, DoWorkEventArgs e)
{
try
{
... ...
}
finally
{
}
return 0;
}
4、實現DoWork方法。
/// <summary>
/// 工作線程所做工作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
e.Result = process(worker, e);
//以下為接受多個參數的方法,一個參數就不用說了吧 :)
//string arg1 = ((ArrayList)e.Argument)[0].ToString();
//string arg2 = ((ArrayList)e.Argument)[1].ToString();
//e.Result = compare(arg1, arg2 , worker, e);
}
5、實現RunWorkerCompleted方法。
/// <summary>
/// 工作線程結束後所做工作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
ShowMessage(ResUtils.getString(("TaskError")));
btnStart.Enabled = true;
btnClose.Enabled = true;
picBox.Enabled = false;
MessageBox.Show(e.Error.Message, SystemContext.APP_TITLE, MessageBoxButtons.OK, MessageBoxIcon.Error);
if (log.IsErrorEnabled)
{
log.Error(e.Error.Message, e.Error);
}
}
else
{
ShowMessage(ResUtils.getString(("TaskSuccess")));
btnStart.Enabled = true;
btnClose.Enabled = true;
picBox.Enabled = false;
}
}
6、啟動後台工作線程。
private void btnStart_Click(object sender, EventArgs e)
{
if (check())
{
btnStart.Enabled = false;
btnClose.Enabled = false;
bgWorker.RunWorkerAsync();//這裡可以添加一個參數對象,如果單個參數直接傳,如果多個參數封裝到類或ArrayList
//以下為傳送多個參數的方法,一個參數就不用說了吧 :)
//ArrayList arg = new ArrayList(2);
//arg.Add(txtExcelFile.Text);
//arg.Add(cmbReportType.Text);
//bgWorker.RunWorkerAsync(arg); //暈啊,傳多個參數竟然非要用數組或者對象
}
}
7、最後,同樣,要引入的類為:
using System.Threading;