C# 什么';取消长期手术的最佳方法是什么?

C# 什么';取消长期手术的最佳方法是什么?,c#,multithreading,process,backgroundworker,C#,Multithreading,Process,Backgroundworker,我遇到了一个我不知道如何解决的问题。我有一个方法,其中包含来自填充数据表的服务的调用,例如: private void GetCases() { try { //Setup our criteria Web_search_criteria myCriteria = new Web_search_criteria() { Keynum = 9, //Use Opening Date Key

我遇到了一个我不知道如何解决的问题。我有一个方法,其中包含来自填充数据表的服务的调用,例如:

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检查进度时才会起作用

我曾经考虑过使用线程而不是后台工作人员,但我已经读到中止线程会导致小猫自燃


那么,对于我来说,用户可以取消一个长进程的最好方法是什么呢?

如果你不想中止线程(这确实会导致世界崩溃和小猫自焚),那么结束线程的唯一明智的方法就是对线程内的工作进行粒化,并经常检查取消令牌(以您喜欢的任何形式:内置的[用于TPL]、消息、锁定的变量、信号量或其他任何形式)并“干净地”结束线程.

没有正确取消此操作的基本方法。这不是CPU绑定的工作,而是网络绑定的工作。您发送了请求,无法从internet上准确地提取该请求。您真正能做的就是在满足取消条件时继续执行代码,而不是等待操作完成h

您的应用程序的一个主要问题是您在消息队列中使用的busylop:

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
,这样它就可以返回它从数据库中获得的
DataTable
,而不是修改实例字段。然后你可以通过任务的
结果来访问它,以防小猫自燃。(现在有一行我从来没想到会在互联网上出现。)您使用的是.NET/C#的哪个版本?如果.NET 4.0/4.5,您可以尝试使用包含取消令牌支持的任务。@谢谢您的建议。我使用的是4.0,因此我将查看tasks类以了解其工作原理。值得思考的是:这可能不符合您应用程序的目标,并且肯定会改变用户经验,但你也可以考虑分页。如果他们给出的日期范围大于XX天,你只需要30天,并且有更多或下一个按钮来获取更多的数据。或者在完成后可以自动地链接30天的请求,所以当你爬到满3年的数据时,你立即显示数据。问题,但从另一个角度来看问题。@user1949119您遇到了什么问题?不知道是什么问题,我无话可说。非常感谢您的解释和示例代码。但是,我遇到了t.Result的问题—“System.Threading.Tasks.Task”没有包含“Result”和“n”的定义o可以找到接受类型为“System.Threading.Tasks.Task”的第一个参数的扩展方法“Result”(是否缺少using指令或程序集引用?)@user1949119查看我的最后一段。我建议
GetCases
应该返回一个结果,这意味着任务将有一个结果。如果不这样做,它就没有结果,因此您不需要使用它。虽然您可以不使用结果,但首选的解决方案是确保任务有一个结果by修改
GetCases
。此外,TPL还支持C#5中的异步和等待,这确实有助于清理问题code@user1949119您可能在某个地方阻止了UI线程。它不在您显示的代码中,可能在其他地方。您在某个时间阻止了应该执行异步操作的位置不客气地。