有時(shí)在程序中需要一些占用資源很大的處理,比如數(shù)據(jù)庫(kù)更新操作,在一些比較簡(jiǎn)單的情況可以使用Application.DoEvent()方法,解決UI界面不友好的問(wèn)題,但是如果是一些復(fù)雜的情況就沒(méi)辦法了,比如在處理中點(diǎn)擊按鈕以暫停當(dāng)前處理。這時(shí)用第一個(gè)方法就不好了(因?yàn)榘粹o不會(huì)顯示被按下)
這個(gè)時(shí)候就需要用異步委托或者啟動(dòng)另一個(gè)線(xiàn)程去處理復(fù)雜的聚集操作,但是如果在工作線(xiàn)程中需要更新用戶(hù)的UI界面,并且在點(diǎn)擊按鈕后要停止那些工作線(xiàn)程,需要如何進(jìn)行處理呢?
解決第一個(gè)問(wèn)題的方法,就是在工作線(xiàn)程中調(diào)用Control.BeginInvoke方法(Control.Invoke用于同步調(diào)用,該調(diào)用阻塞當(dāng)前線(xiàn)程直到UI返回結(jié)果)。
Control.BeginInvoke方法有一個(gè)重載方法
public IAsyncResult BeginInvoke(Delegate);
public virtual IAsyncResult BeginInvoke(Delegate, object[]);
第二個(gè)方法傳遞的是委托的參數(shù)
在工作線(xiàn)程中調(diào)用的方法中可以先判斷是否在UI線(xiàn)程中,如果是就進(jìn)行UI操作,如果不是在進(jìn)行異步調(diào)用(control.begininvoke),在這里需要用到一個(gè)判斷條件:Control.InvokeRequired,這個(gè)屬性如果返回False,則代表當(dāng)前就是UI線(xiàn)程,否則為工作線(xiàn)程
舉個(gè)例子:
private void MakeLogs(string body)
{
if (InvokeRequired == false)
{
ListViewItem lvi = new ListViewItem(DateTime.Now.ToString());
lvi.SubItems.Add(body);
if (lvContent.Items.Count == 200)
{
lvContent.Items.RemoveAt(lvContent.Items.Count-1);
}
lvContent.Items.Insert(0,lvi);
}
else
{
MakeLogDelegate makeLogs = new MakeLogDelegate(MakeLogs);
BeginInvoke(makeLogs,new object[]{body});
}
}
解決第二個(gè)問(wèn)題的方法是:在工作線(xiàn)程中周期性的檢查按鈕狀態(tài)。比如界面有個(gè)“取消”按鈕,當(dāng)用戶(hù)點(diǎn)擊這個(gè)按鈕后,將狀態(tài)變量置位,然后工作線(xiàn)程就去檢查這個(gè)狀態(tài)變量,如果發(fā)現(xiàn)取消狀態(tài)被置位,則停止當(dāng)前線(xiàn)程
舉個(gè)例子:
private void CheckState(out bool cancel)
{
if (InvokeRequired == false)
//在此需要判斷線(xiàn)程是否為UI線(xiàn)程,因?yàn)槿绻诠ぷ?br />
//線(xiàn)程上,會(huì)導(dǎo)致兩個(gè)線(xiàn)程(UI Thread、Work Thread)共同訪問(wèn)一個(gè)變量
{
cancel = (m_state==ImportState.canceled);
}
else
{
object outCancel = false; //此處的用意是:由于bool類(lèi)型是值類(lèi)型,
//在傳入后進(jìn)行裝箱操作,導(dǎo)致其值不變化
CheckStateDelegate checkStateDelegate = new CheckStateDelegate(CheckState);
Invoke(checkStateDelegate,new object[]{outCancel});
//此處用Invoke而不用BeginInvoke的原因是:需要確實(shí)得到狀態(tài)才
//能做返回,所以不能用異步調(diào)用
cancel = (bool)outCancel;
}
}