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

I have run into a problem that I'm not sure how to resolve. I have a method that contains a call from a service that populates a data table such as:

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);
    }
}

So, as you can see, there isn't a way for me to grab just a few cases at a time - it just grabs it all.

Right now, I have this method being called in a BackgroundWorker's DoWork event, and I display another form with a marquee progress bar on it so the user will know that the system is actually doing something. On that form, I have a Cancel button that I subscribe to. Here is the code that does that:

    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();
    }

I have tried to cancel the BackgroundWorker in the CancelBW event by using CancelAsync(), but then I read more and realize that doesn't work and would only work if I could break up that initial call so the BackgroundWorker could check on progress.

I have thought about using Thread instead of a BackgroundWorker, but have read that Aborting a thread will cause kittens to spontaneously combust.

So, what's the best way for me to handle this in which a user could cancel a long process?


There is no fundamental way to properly cancel this operation. It's not CPU bound work, it's network bound work. You sent the request, you can't exactly pluck that request from the internet. All you can really do is have your code continue executing when your cancel conditions are met instead of waiting around for the operation to finish.

One major issue with your application is the busyloop that you have pumping the message queue:

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

This is generally a bad idea, and is a practice that should be avoided. The idea of using a BackgroundWorker in the first place is for it to be asynchronous; you shouldn't be trying to block the current method until the BGW finishes.

While there are ways of incorporating a BGW into this; this particular case it probably easier to solve using the Task Parallel Library.

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.
    });

(I'd also suggest refactoring GetCases so that it returns the DataTable that it gets from the DB, rather than modifying an instance field. You can then access it through the Task's Result .


如果你不想放弃线程(这实际上会导致世界崩溃并且小猫在燃烧),结束一个线程的唯一理智的方法是对其内部的工作进行细化,并且对取消标记进行频繁检查(在任何情况下形式上你更喜欢他们:内置于[对于TPL],消息,锁定变量,信号量或其他),并以“干净地”结束线程。

链接地址: http://www.djcxy.com/p/18096.html

上一篇: 是否有可能使用C#在控制台中绘制图像?

下一篇: 取消长时间操作的最佳方法是什么?