C# 如何取消任务但等待任务完成?

C# 如何取消任务但等待任务完成?,c#,.net,multithreading,task-parallel-library,C#,.net,Multithreading,Task Parallel Library,我有一个线程任务,它在循环中执行一些操作: static void TaskAction(CancellationToken ct) { while (SomeCondition()) { DoSomeSeriousJob(); ct.ThrowIfCancellationRequested(); } } static void DoSomeSeriousJob() { Console.WriteLine("Serious job

我有一个线程任务,它在循环中执行一些操作:

static void TaskAction(CancellationToken ct)
{
    while (SomeCondition())
    {
        DoSomeSeriousJob();
        ct.ThrowIfCancellationRequested();
    }
}

static void DoSomeSeriousJob()
{
    Console.WriteLine("Serious job started");
    Thread.Sleep(5000);
    Console.WriteLine("Serious job done");
}
我启动它,然后在一段时间后取消:

    var cts = new CancellationTokenSource();
    var task = Task.Factory.StartNew(() => TaskAction(cts.Token), cts.Token);
    Thread.Sleep(1000);
    cts.Cancel();
这个操作必须正确完成,我不想打断它。但是我想向我的任务发送一个取消请求,并等待它正确地完成(我的意思是它到达代码中的某个点)。 我尝试了以下方法:

1.等待(取消令牌ct) 在这种情况下,程序立即从
Wait()
返回。任务将继续运行,直到通过wifcancellationrequested(),但如果主线程退出,任务也会中断

2.等等 在这里,主线程等待完成,但在最后,
aggregateeexception
随着
InnerException
=
taskcancelledeexception
(而不是
operationcancelledeexception
)上升

3.选中IsCancellationRequested(),无异常 在这种情况下,不会出现异常,但任务最终会获得状态
RanToCompletion
。当
SomeCodition()
开始返回false时,这与正确完成没有区别


所有这些问题都有简单的解决方法,但我想知道,可能我遗漏了什么?有谁能给我提供更好的解决方案吗?

如果您想等待任务同步完成(或取消),可以尝试以下方法:

cts.Cancel();
Task.Run(async () => {
    try {
        await task;
    }
    catch (OperationCanceledException ex) {
      // ...
    }
).Wait();
因此,您可以直接捕获OperationCanceledException,而不是捕获AggregateException

编辑:

等待(Caneclationtoken)

这种方法无法达到这个目的。 声明:

等待任务完成执行。如果在任务完成之前取消了取消令牌,则等待将终止

等待()

您可以使用这种方法,但正如您所看到的,您应该期望AggregateException而不是OperationCanceledException。它也在方法的第1部分中指定

AggregateException.InnerException集合包含TaskCanceledException对象

所以在这种方法中,为了确保操作被取消,您可以检查内部expection是否包含TaskCanceledException

检查IsCancellationRequested(),无异常

通过这种方式,很明显没有抛出异常,并且您无法确定操作是否已取消

如果不想同步等待,则一切都会按预期进行:

cts.Cancel();
try {
    await task;
}
catch (OperationCanceledException ex) {
   // ...
}
试试这个:

try
{
    task.GetAwaiter().GetResult();
}
catch (OperationCanceledException)
{
    Console.WriteLine("Task cancelled");
}

您将获得一个
OperationCanceledException
,它不会被
AggregateException

包装。如果您将取消令牌传递给
Wait
,它将取消等待。如果要等待任务完成,请不要将令牌传递给
Wait
。Re 2,
TaskCanceledException
派生自
OperationCanceledException
。如果您使用了
Wait
而不是
Wait
,那么
aggregateeexception
将被打开,您可以捕获
InnerException
。juharr,这是我的案例1。如果不等待,调用
Wait(ct)
有什么用?明白了。如果令牌被取消,它将等待完成或引发
OperationCancelledException
,如果令牌被取消,它将不带参数地等待完成并引发
AggregateException
。在情况3中,如果您在观察到已请求取消后离开操作,你仍然认为这个方法已经完成了吗?要区分优雅的执行和取消的执行,可以使用
CancellationToken.ThrowIfCancellationRequested
。没有我想要的那么漂亮,但很有趣:)是的,漂亮的方式是异步等待。它可能会工作,但原因不明,声明“此方法旨在供编译器使用,而不是在应用程序代码中使用。“@MehrzadChehraz,MSDN文档也有其不完善之处和含沙射影。这种方法被广泛使用,而且不会有任何进展,因为它是@MehrzadChehraz的一部分。至于您的方法,
Task.Run
显然是多余的。您最好只执行
(newfunc(async()=>{…}))()。等待()
cts.Cancel();
Task.Run(async () => {
    try {
        await task;
    }
    catch (OperationCanceledException ex) {
      // ...
    }
).Wait();
cts.Cancel();
try {
    await task;
}
catch (OperationCanceledException ex) {
   // ...
}
try
{
    task.GetAwaiter().GetResult();
}
catch (OperationCanceledException)
{
    Console.WriteLine("Task cancelled");
}