如何取消长时间运行但单线程的操作

时间:2014-07-22 19:27:59

标签: c# winforms cancellation

我在C#中看到很多取消长时间运行操作的选项,但是每个例子似乎都谈到取消并行(多线程)操作或者过于简单的例子,或者涉及定期轮询是否取消请求操作已提交。我认为这不会起作用。

我有一个方法BuildZipFile(),目前它没有参数,但我怀疑可能需要一个CancellationToken参数。调用此方法执行以下操作。 BuildZipFile()块;线程上的执行不会恢复,直到完成它的工作。

  1. 提取文件并将其添加到zip文件中。此操作非常快,我不希望它被取消。如果用户请求取消,则应该在操作完成之前忽略该请求,然后跳过剩余的BuildZipFile()并返回(或抛出异常;无关紧要。)

    < / LI>
  2. 使用称为&#34;管道的东西处理文件。&#34;此操作 需要很长时间,用户应该可以取消它。要开始此处理,BuildZipFile()会在管道上调用非阻塞方法Start()。管道在完成其工作后会引发Done,因此我使用AutoResetEvent来阻止该方法,直到我听到该事件,然后释放该块。

  3. 类似于第1项的更多操作:不应支持取消的快速运行操作。

  4. 这是一个过于简化的实施:

    public void BuildZipFile()
    {
        // single-threaded operation that is quick and can't be canceled
        DoQuickUncancelableThings();
    
        // and now a long-running operation that the user SHOULD be able to cancel;
        // it must be possible to interrupt the AutoResetEvent
        var pipeline = GimmeAPipeline();
        var reset = new AutoResetEvent(false);
        // when the pipeline raises Done, stop blocking the method and resume execution
        pipeline.Done += () => reset.Set(); 
        // define the work to be done
        ThreadPool.QueueUserWorkItem(state => pipeline.Start());
        // call pipeline.Start() and block the thread until pipeline.Done is raised
        reset.WaitOne();
    
        // ...and more quick operations that can't be canceled
        DoMoreQuickUncancelableThings();
    }
    

    请注意,实际上,该中间代码块存在于此调用的另一个类中。

    我可以通过调用pipeline.Stop()来停止此方法中的管道,这会在处理停止它的请求后间接引发Done事件。

    那么,如何修改BuildZipFile()以支持用户取消?我的直觉是添加对OperationCanceledException的支持,但这样可以让那些快速操作取消,不是吗?并且,我无法轮询取消请求,除非我错过了某些内容,因为我等待Done来自pipeline的{​​{1}}事件被提出,最后一件事我想做的是使用计时器进行轮询来中断它。

    我没有将BuildZipFile()修改为非阻塞的问题,但其中的步骤非常线性。步骤#2甚至无法开始直到步骤#1完成;这个过程不能平行。我不能改变管道的工作方式;他们必须保持异步并在事件发生时提出事件。

    我在Windows窗体应用程序中使用.NET 4.5,因此我可以使用我需要的任何框架功能。

1 个答案:

答案 0 :(得分:0)

我认为你应该使用任务来做你想要的。 查看这篇msdn文章,它非常实用

http://msdn.microsoft.com/en-us/library/dd537609(v=vs.110).aspx

以下是控制台应用程序中的完整示例

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication4
{
 class Program
 {
    static CancellationTokenSource tokenSource2;
    static CancellationToken ct;
    static void Main(string[] args)
    {
        tokenSource2 = new CancellationTokenSource();
        ct = tokenSource2.Token;
        Task myTask = new Task(BuildZipFile);
        myTask.Start();

        Console.WriteLine("Press enter to cancel");
        Console.ReadLine();
        tokenSource2.Cancel();
        Console.ReadLine();
    }

    public static void BuildZipFile()
    {
        Task quick1 = new Task(DoQuickUncancelableThings);
        quick1.ContinueWith(ant => DoLongRunnignThings(), ct).ContinueWith(ant => DoMoreQuickUncancelableThings());
        quick1.Start();
    }

    private static void DoMoreQuickUncancelableThings()
    {
        Console.WriteLine("Q2");
    }
    private static void DoLongRunnignThings()
    {
        for (int i = 0; i < 10; i++)
        {
            System.Threading.Thread.Sleep(1000);
            ct.ThrowIfCancellationRequested();
        }

        Console.WriteLine("Long ended");
    }
    private static void DoQuickUncancelableThings()
    {
        Console.WriteLine("Q1");
    }
 }
}