邊學習邊分享,純屬拋磚引玉。
線程的一個好處是異步的執行操作,在winform中,很多耗時操作執行時,為優化用戶體驗,避免長時間等待,從而運用線程技術異步的執行耗時操作,但不會阻塞主線程。
最近系統很多耗時查詢導致體驗很差,於是想到了用BackGroundWorker異步處理。而且要支持某些耗時達到幾十秒的操作,可以取消。
BackGroundWorker有CancelAsync()這個方法。該方法只會將BackGroundWorker的CancellationPending屬性設為true,但不會實際終止線程!網上找了些資料,代碼如下
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { for (int i = 0; i < 100; i++) { if (backgroundWorker1.CancellationPending) { e.Cancel = true; return; } } }
即在doWork事件中,不停的循環判斷是否執行了取消操作。這種適用於執行多次方法時,而且無法在某個方法執行時將線程取消。如果我doWork事件里,沒有for循環,而是一次耗時的數據訪問操作,那這樣的處理沒有任何意義。
於是在下想到,可以在doWork事件里做如下處理
1.異步的去執行耗時操作。
2.while監視異步耗時操作是否完成,只要未完成就一直循環。循環內部判斷是否執行了取消操作。如果執行了取消操作,則終止線程。代碼如下
private delegate int LongTimeDele(); private bool isComplated=false; private int result=0; private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { this.BeginInvoke(new LongTimeDele(()=>{ Thread.Sleep(10000);////模擬耗時 for (int i = 0; i < 100; i++) { result++; } isComplated=true; })); while(!isComplated){ if (backgroundWorker1.CancellationPending) { e.Cancel = true; return; } } }
試驗之后,不知何故在this.BeginInvoke中的循環會阻塞主線程! 即界面卡主不動了,BeginInvoke應該是一步執行的,在下功力尚淺,不知何故,還請高手指教。
於是想到了異步委托。直接上代碼
cs代碼
public partial class BackgroundWorkerTest : Form { public BackgroundWorkerTest() { InitializeComponent(); } /// <summary> /// 建立一個委托,用來調用耗時操作。返回值是int,當然也可以是string、DataTable..... /// </summary> /// <returns></returns> private delegate int LongTimeDele(); /// <summary> /// doWork事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { //聲明一個委托的實例 var waitHandller = new LongTimeDele( () => { //線程休眠10秒,模擬耗時操作 Thread.Sleep(10000); var sumResult = 0; for (int i = 0; i < 100; i++) { sumResult++; } return sumResult; }); //BeginInvoke,異步的去執行委托實例里的匿名方法。該方法返回的是一個IAsyncResult。返回值的狀態會實時更新! var isa = waitHandller.BeginInvoke(null, null); //由於上面是異步,所以while的代碼不會等待上面操作完成后執行,二者幾乎同時執行 //判斷isa是否完成了,未完成的話(等待耗時操作)執行內部代碼 while (!isa.IsCompleted) { //判斷CancellationPending屬性是否為true。如果你點擊了取消button,這個屬性就變成true了 if (backgroundWorker1.CancellationPending) { //取消操作 e.Cancel = true; return; } } //能執行到這里就說明操作完成了,把結果放進e中,傳遞給RunWorkerCompleted事件 e.Result = waitHandller.EndInvoke(isa); } /// <summary> /// backgroundWorker執行完畢時事件,無論正常完成還是取消都會出發該事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { //只要不是類型EventArgs,你想要的數據e里面應有盡有!判斷是否取消了操作 if (e.Cancelled) { MessageBox.Show("操作已取消"); return; } //否則顯示結果 MessageBox.Show(e.Result.ToString()); } /// <summary> /// 取消按鈕事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnCancle_Click(object sender, EventArgs e) { //調用 backgroundWorker的CancelAsync()方法,該方法只會將CancellationPending屬性設為true,並不會實際結束線程執行 backgroundWorker1.CancelAsync(); } /// <summary> /// 開始按鈕事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnStart_Click(object sender, EventArgs e) { //判斷線程是否繁忙 if (backgroundWorker1.IsBusy) { MessageBox.Show("系統繁忙"); return; } //空閑開始異步操作 backgroundWorker1.RunWorkerAsync(); } }