繁体   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