異步委托方式取消BackGroundWorker執行無循環的耗時方法


邊學習邊分享,純屬拋磚引玉。

線程的一個好處是異步的執行操作,在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();
        }
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM