我在C#中看到很多取消长时间运行操作的选项,但是每个例子似乎都谈到取消并行(多线程)操作或者过于简单的例子,或者涉及定期轮询是否取消请求操作已提交。我认为这不会起作用。
我有一个方法BuildZipFile()
,目前它没有参数,但我怀疑可能需要一个CancellationToken
参数。调用此方法执行以下操作。 BuildZipFile()
块;线程上的执行不会恢复,直到完成它的工作。
提取文件并将其添加到zip文件中。此操作非常快,我不希望它被取消。如果用户请求取消,则应该在操作完成之前忽略该请求,然后跳过剩余的BuildZipFile()
并返回(或抛出异常;无关紧要。)
使用称为"管道的东西处理文件。"此操作 需要很长时间,用户应该可以取消它。要开始此处理,BuildZipFile()
会在管道上调用非阻塞方法Start()
。管道在完成其工作后会引发Done
,因此我使用AutoResetEvent
来阻止该方法,直到我听到该事件,然后释放该块。
类似于第1项的更多操作:不应支持取消的快速运行操作。
这是一个过于简化的实施:
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,因此我可以使用我需要的任何框架功能。
答案 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");
}
}
}