Task.ContinueWith 和异步(核心 3.1)
Posted
技术标签:
【中文标题】Task.ContinueWith 和异步(核心 3.1)【英文标题】:Task.ContinueWith and async (core 3.1) 【发布时间】:2022-01-22 10:50:15 【问题描述】:我的代码是这样的:
public async Process()
try
var records = await this.provider.GetRecordsAsync(); // this method can throws 'DataStoreException' only
foreach(var record in records)
try
this.Do(record); // here is some exception and code goes to catch
record.Status = Status.Success;
await this.provider.UpdateAsync(record).ContinueWith(
task =>
task.Exception?.Handle(e =>
this.logger.LogError(e);
return true;
);
, TaskContinuationOptions.NotOnRanToCompletion);
catch(Exception e)
record.Status = Status.Error;
/*line 106:*/ await this.provider.UpdateAsync(record).ContinueWith(
task =>
task.Exception?.Handle(e =>
this.logger.LogError(e);
return true;
);
, TaskContinuationOptions.NotOnRanToCompletion);
catch (DataStoreException e)
this.logger.LogError(e);
catch (Exception e)
// Got TaskCanceledException here
在评论点我得到了TaskCanceledException
。我想原因是混合await
和ContinueWith
,但是有人可以解释一下,这里发生了什么? UpdateAsync
只能抛出 DataStoreException
。所以TaskCanceledException
thew by ContinueWith
,但是没有取消令牌,代码甚至没有达到这一点task.Exception?.Handle
这是调用堆栈:
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at ProjectTest.MyClass.<Process>d__6.MoveNext() in C:\Projects\ProjectTest\MyClass.cs:line 106
如果 try/catch
包围 this.provider.UpdateAsync(record)
而不是 ContinueWith
一切都很好并且代码按预期工作。
将await
和ContinueWith
混合使用真的是不好的做法吗?如果是,为什么?
【问题讨论】:
附带说明一下,您熟悉finally
块吗?
除非您是专家并且有充分的理由,否则不要将ContinueWith
与async-await
混为一谈。
【参考方案1】:
您看到的行为可以更简单地重现:
await Task.CompletedTask.ContinueWith(task =>
, TaskContinuationOptions.NotOnRanToCompletion);
TaskContinuationOptions.NotOnRanToCompletion
指示ContinueWith
方法在任务成功完成时不执行回调,而是返回已取消的任务。
如果您有一个特别希望在发生错误时运行的延续,您将使用该选项。例如:
await Task.FromException(new Exception()).ContinueWith(task =>
Console.WriteLine("Task did not execute to completion");
, TaskContinuationOptions.NotOnRanToCompletion);
由于您认为this.provider.UpdateAsync(record)
可能会成功,因此这可能不是您想要使用的选项。
【讨论】:
问题的第二个目标——有没有关于ContinueWith
和await
的好帖子?我发现的都是肤浅的。附言当然我熟悉try/finally
,上面的代码只是一个例子,实际代码有不同的异常处理:)
@Jonik:我没有特别推荐的帖子。 Paulo 是对的:async
/await
应该是您的默认设置。它不仅使您的代码更加惯用和直接:它在后台执行了许多编译魔术,以确保处理堆栈跟踪之类的事情比您实际处理的更好。如果您曾经做过非常复杂的事情,因此您需要手动管理任务延续,那么您需要阅读所有文档并了解 ContinueWith 等方法的来龙去脉。以上是关于Task.ContinueWith 和异步(核心 3.1)的主要内容,如果未能解决你的问题,请参考以下文章
在 Task ContinueWith TaskScheduler.FromCurrentSynchronizationContext 的 ShowDialog 中打开表单时,应用程序会冻结