Rev*_*1.0 16 c# asynchronous task
考虑以下代码:
MyTask = LongRunningMethod(progressReporter, CancelSource.Token)
.ContinueWith(e =>
{ Log.Info("OnlyOnCanceled"); },
default,
TaskContinuationOptions.OnlyOnCanceled,
TaskScheduler.FromCurrentSynchronizationContext())
.ContinueWith(e =>
{ Log.Info("OnlyOnFaulted"); },
default,
TaskContinuationOptions.OnlyOnFaulted,
TaskScheduler.FromCurrentSynchronizationContext())
.ContinueWith(e =>
{ Log.Info("OnlyOnRanToCompletion"); },
default,
TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.FromCurrentSynchronizationContext())
.ContinueWith(e =>
{ Log.Info("None"); },
default,
TaskContinuationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
Run Code Online (Sandbox Code Playgroud)
当我使用提供的 CancelSource 取消任务时,输出为:
OnlyOnCanceled
None
正如预期的那样。
当 LongRunningMethod 抛出异常时,输出为:
OnlyOnFaulted
None
正如预期的那样。
当LongRunningMethod完成输出是:
None
所以ContinueWithwithTaskContinuationOptions.OnlyOnRanToCompletion没有像我期望的那样执行。
我检查MyTask.Status了最后一个ContinueWith分支,它仍然是Running. 因此,考虑到这一点,我希望跳过 OnlyOnRanToCompletion。问题是,为什么是Statusstill Running?查看调试输出,我可以看到它LongRunningMethod一直运行到最后。
Ros*_*utt 10
看起来您正在将延续任务相互链接起来,而不是全部从原始任务中分离出来。这意味着您的 TaskContinuationOptions 指的是链中上一个任务的完成状态,而不是原始父级 (MyTask)。
我会尝试类似以下的东西(我不能在本地尝试这个确切的版本,因为我没有你的所有功能,但类似的东西对我有用)。
MyTask = LongRunningMethod(mods, Settings, progressReporter, CancelSource.Token);
MyTask.ContinueWith(e =>
{
Log.Info("OnlyOnCanceled");
}, default ,TaskContinuationOptions.OnlyOnCanceled, TaskScheduler.FromCurrentSynchronizationContext());
MyTask.ContinueWith(e =>
{
Log.Info("OnlyOnFaulted");
}, default ,TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
MyTask.ContinueWith(e =>
{
Log.Info("OnlyOnRanToCompletion");
}, default ,TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext());
MyTask.ContinueWith(e =>
{
Log.Info("None");
}, default ,TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
Run Code Online (Sandbox Code Playgroud)
这给了我:
OnlyOnRanToCompletion
None
Run Code Online (Sandbox Code Playgroud)
正如文档中所写:
您可以指定延续仅在前提成功完成时才运行,或者仅当它在故障状态下完成时才运行。如果在前提准备好调用延续时条件不为真,则延续直接转换到 TaskStatus.Canceled 状态,随后无法启动。
这意味着链接ContinueWith调用在您的情况下不起作用,因为如果第一次继续与实际任务状态不匹配,它将返回取消的任务到下一个链接调用。
您可以Task在此代码段中检查更改的结果和重新排序的延续:
var task =
//Task.FromCanceled(new CancellationToken(true))
Task.FromException(new Exception())
//Task.CompletedTask
.ContinueWith(e => Console.WriteLine("OnlyOnCanceled"), TaskContinuationOptions.OnlyOnCanceled)
.ContinueWith(e => Console.WriteLine("OnlyOnFaulted"), TaskContinuationOptions.OnlyOnFaulted)
.ContinueWith(e => Console.WriteLine("OnlyOnRanToCompletion"), TaskContinuationOptions.OnlyOnRanToCompletion);
Task.WaitAny(task); // to wait for task without exception and async
Console.WriteLine(task.Status);
Run Code Online (Sandbox Code Playgroud)
此外,设置多个单独的延续可能不是最佳解决方案,因为实际上您只需要一个任务时,您将产生多个任务。
同一文档的“将数据传递到延续”部分建议分析Task.Status先行词的属性,例如:
Task.FromResult(1)
.ContinueWith(t =>
{
switch (t.Status)
{
case TaskStatus.RanToCompletion: Console.WriteLine("OnlyOnRanToCompletion"); return t.Result;
case TaskStatus.Canceled: Console.WriteLine("OnlyOnCanceled"); return default;
case TaskStatus.Faulted: Console.WriteLine("OnlyOnFaulted"); return default;
default: return default;
}
});
Run Code Online (Sandbox Code Playgroud)
使用async-await进行日志记录可能会更简单。这样您就可以避免老式ContinueWith方法及其令人困惑的参数和行为。
public static async void OnCompletionLog(this Task task, string name)
{
try
{
await task;
Log.Info($"{name} RanToCompletion");
}
catch (OperationCanceledException)
{
Log.Info($"{name} Canceled");
}
catch (Exception ex)
{
Log.Error(ex, $"{name} Failed");
}
}
Run Code Online (Sandbox Code Playgroud)