简体   繁体   English

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

[英]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. 现在,我在BackgroundWorker的DoWork事件中调用了这个方法,并在其上显示另一个带有选取框进度条的表单,以便用户知道系统实际上正在执行某些操作。 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. 我试图通过使用CancelAsync()取消CancelBW事件中的BackgroundWorker,但后来我读了更多并意识到这不起作用,只有在我打破初始调用以便BackgroundWorker可以检查进度时才能工作。

I have thought about using Thread instead of a BackgroundWorker, but have read that Aborting a thread will cause kittens to spontaneously combust. 我曾考虑使用Thread而不是BackgroundWorker,但已经读过,中止一个线程会导致小猫自发燃烧。

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. 这不是CPU限制工作,而是网络绑定工作。 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: 您的应用程序的一个主要问题是您已填充消息队列的busyloop:

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; 首先使用BackgroundWorker的想法是它是异步的; you shouldn't be trying to block the current method until the BGW finishes. 在BGW完成之前,你不应该试图阻止当前的方法。

While there are ways of incorporating a BGW into this; 虽然有办法将BGW纳入其中; 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 . (我还建议重构GetCases以便它返回从DB获取的DataTable ,而不是修改实例字段。然后您可以通过Task的Result访问它。

如果你不想中止线程(实际上,它会导致世界崩溃和小猫燃烧),结束线程的唯一理智方法是对其中的工作进行粒化,并对取消令牌进行频繁检查(以其中任何一个为准)你喜欢它们的形式:内置[用于TPL],消息,锁定变量,信号量等等,并“干净地”结束线程。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在方法中的任何地方取消操作的最佳方法是什么? - What is the best way to cancel operation anywhere in a method? 使用一次性对象回调取消操作的正确方法是什么? - What's the correct way to cancel an operation with a callback on a disposable object? 使用任务取消长时间运行的 IO/网络操作的好方法是什么? - What would be a good way to Cancel long running IO/Network operation using Tasks? 取消处于阻塞状态的任务的最佳方法是什么? - What is the best way to cancel a task that is in a blocking state? 取消套接字接收请求的最佳方法是什么? - What is the best way to cancel a socket receive request? 在 web 表单上取消任务的最佳方法是什么 - What is the best way to cancel a task on a web form 在 backgroundworker 中取消长时间运行的进程的最佳方法 - Best way to cancel long running process inside backgroundworker 取消客户端上的长期运行取消 - Cancel long running operation on client cancel 取消不接受CancellationToken的异步操作的正确方法是什么? - What is the correct way to cancel an async operation that doesn't accept a CancellationToken? 从C#执行添加/更新数据库操作的最佳方法是什么? - What's the best way to do an Add/Update DB operation from C#?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM