簡體   English   中英

取消長時間操作的最佳方法是什么?

[英]What's the best way to cancel a long operation?

我遇到了一個我不確定如何解決的問題。 我有一個方法,其中包含來自填充數據表的服務的調用,例如:

private void GetCases()
{
    try
    {
        //Setup our criteria
        Web_search_criteria myCriteria = new Web_search_criteria()
        {
            Keynum = 9, //Use Opening Date Key
            Range_start = "20100101", //01-01-2010
            Range_end = "20121223" //12-23-2013
        };


        //The myCases object is a datatable that is populated from the GetCasesDT() call.
        int status = db.server.GetCasesDT(myCriteria, ref myCases);
    }
    catch (Exception ex)
    {
        XtraMessageBox.Show("Unable to get data: " + ex.Message);
    }
}

所以,正如你所看到的,我沒有辦法一次抓住幾個案例 - 它只是抓住了一切。

現在,我在BackgroundWorker的DoWork事件中調用了這個方法,並在其上顯示另一個帶有選取框進度條的表單,以便用戶知道系統實際上正在執行某些操作。 在那個表單上,我有一個我訂閱的取消按鈕。 這是執行此操作的代碼:

    backgroundWorker1 = new BackgroundWorker() 
    { 
        WorkerSupportsCancellation = true,
        WorkerReportsProgress = true
    };

    //DoWork Event
    backgroundWorker1.DoWork += backgroundWorker1_DoWork;

    //Show the progress bar with subscription to event
    pb.btnCancel.Click += this.CancelBW;
    pb.Show();

    //Run the backgroundworker
    this.backgroundWorker1.RunWorkerAsync();

    //Don't lock up the application
    while (this.backgroundWorker1.IsBusy)
    {
        Application.DoEvents();
    }

我試圖通過使用CancelAsync()取消CancelBW事件中的BackgroundWorker,但后來我讀了更多並意識到這不起作用,只有在我打破初始調用以便BackgroundWorker可以檢查進度時才能工作。

我曾考慮使用Thread而不是BackgroundWorker,但已經讀過,中止一個線程會導致小貓自發燃燒。

那么,對我來說,處理這種情況的最佳方式是什么,用戶可以取消漫長的過程?

沒有基本的方法可以正確取消此操作。 這不是CPU限制工作,而是網絡綁定工作。 您發送了請求,您無法從互聯網上完全采摘該請求。 您所能做的就是讓您的代碼在滿足取消條件時繼續執行,而不是等待操作完成。

您的應用程序的一個主要問題是您已填充消息隊列的busyloop:

while (this.backgroundWorker1.IsBusy)
{
    Application.DoEvents();
}

這通常是一個壞主意,是一種應該避免的做法。 首先使用BackgroundWorker的想法是它是異步的; 在BGW完成之前,你不應該試圖阻止當前的方法。

雖然有辦法將BGW納入其中; 在這種特殊情況下,使用任務並行庫可能更容易解決。

var cts = new CancellationTokenSource();

pb.btnCancel.Click += (s, e) => cts.Cancel();
pb.Show();

var task = Task.Factory.StartNew(() => GetCases())
    .ContinueWith(t => t.Result, cts.Token)
    .ContinueWith(t =>
    {
        //TODO do whatever you want after the operation finishes or is cancelled;
        //use t.IsCanceled to see if it was canceled or not.
    });

(我還建議重構GetCases以便它返回從DB獲取的DataTable ,而不是修改實例字段。然后您可以通過Task的Result訪問它。

如果你不想中止線程(實際上,它會導致世界崩潰和小貓燃燒),結束線程的唯一理智方法是對其中的工作進行粒化,並對取消令牌進行頻繁檢查(以其中任何一個為准)你喜歡它們的形式:內置[用於TPL],消息,鎖定變量,信號量等等,並“干凈地”結束線程。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM