对于初学者,您可能没有在 .NET 4.0 上工作,因为 .NET 4.x 运行时是二进制替代品。安装 .NET 4.5+ 应用程序或 Windows 更新补丁意味着所有应用程序现在都可以在 .NET 4.5 及更高版本上运行。您的开发机器至少可以在 .NET 4.5+ 上运行,这意味着您已经在与您打算定位的运行时不同的运行时上进行开发。
唯一的例外是,如果您面向不受支持的操作系统版本,例如 Windows XP 和 Windows Server 2003,它们从未获得 .NET 4.5 支持。
无论如何,使用Microsoft.Bcl.Async 是 一个可行的选择,因为您已经接受了更大的风险,例如在 4.5 上运行而以 4.0 为目标,或者在不受支持的操作系统上运行甚至不支持 TLS1.2,这是当今大多数服务的最低要求,包括 Google、AWS、Azure、银行、支付网关、航空公司等。
另一个选项是将ContinueWith 与TaskScheduler 参数一起使用,该参数指定在何处运行延续。 TaskScheduler.FromCurrentSynchronizationContext() 指定延续将在原始同步上下文上运行。在 Winforms 或 WPF 应用程序中,这就是 UI 线程。
那时候我们经常写这样的代码:
Task.Factory.StartNew(() => LongerOperation())
.ContinueWith(t =>
{
textBox.Text=String.Format("The result is {0}",t.Result);
},TaskScheduler.FromCurrentSynchronizationContext());
处理异常和链接多个异步操作需要额外的工作。如果任务失败,t.Result 会抛出异常,所以你需要在尝试使用它的值之前检查Task.IsFaulted 或Task.IsCancelled。
没有办法像使用 .NET 4.5 和 async/await 那样使用简单的 return 来缩短延续链。您可以检查 IsFaulted 标志并避免更新 UI,但您不能阻止 next 继续沿链执行。
Task.Factory.StartNew(() => LongerOperation())
.ContinueWith(t=>
{
if (!t.IsFaulted)
{
return anotherLongOperation(t.Result);
}
else
{
//?? What do we do here?
//Pass the buck further down.
return Task.FromException(t.Exception);
}
})
.ContinueWith(t =>
{
//We probably need `Unwrap()` here. Can't remember
var result=t.Unwrap().Result;
textBox.Text=String.Format("The result is {0}",result);
},TaskScheduler.FromCurrentSynchronizationContext());
要在失败的情况下停止执行,您必须使用TaskContinuationOptions 参数并传递例如NotOnFaulted。
Task.Factory.StartNew(() => LongerOperation())
.ContinueWith(t =>
{
textBox.Text=String.Format("The result is {0}",t.Result);
},
CancellationToken.None,
TaskContinuationOptions.NotOnFaulted,
TaskScheduler.FromCurrentSynchronizationContext());
如果由于某些业务规则而想结束执行,您会怎么做?您必须返回 一些东西,所有后续步骤都需要转发到链的末尾。
最后,使用Microsoft.Bcl.Async 可以得到更简单的代码
Task.Factory.StartNew(() => LongerOperation())
.ContinueWith(t=>
{
if (!t.IsFaulted)
{
var num=anotherLongOperation(t.Result);
if (num<0)
{
//Now what?
//Let's return a "magic" value
return Task.FromResult(null);
}
}
else
{
//?? What do we do here?
//Pass the buck further down.
return Task.FromException(t.Exception);
}
})
.ContinueWith(t =>
{
//This may need t.Result.Result
if (!t.IsFaulted && t.Result!=null)
{
textBox.Text=String.Format("The result is {0}",t.Result);
}
},TaskScheduler.FromCurrentSynchronizationContext());
我不确定后续是否需要Unwrap()。他们可能会这样做,因为第二步返回Task<T>。忘记这一点也会导致难以调试的问题。
使用Microsoft.Bcl.Async 可以得到更干净、更简单的代码,而且更容易正确:
public async Task DoRun()
{
try
{
var x=await longOperation();
var num=await anotherLongOperation(x);
if(num<0)
{
return;
}
textBox.Text=String.Format("The result is {0}",num);
}
catch(Exception exc)
{
//Do something about it
}
}
更新
正如 Stephen Cleary 所指出的,BLC Async 仅适用于 Visual Studio 2012。它也不再受支持,并且可能仅作为 MSDN 订阅者的下载提供。
早在 2010 年,Parallel 团队就发布了一些扩展和示例,以简化并行和异步处理,Parallel Extensions Extras。该库包含像 Then 这样的外部组件,它使链接和错误处理变得更加容易。
其中大部分功能自 4.5 起已包含在 .NET 本身中,因此示例和库自 2011 年以来未更新。任何声称是 Parallel Extras 的 NuGet 包或 Github 存储库只需克隆并重新打包此下载。
该库曾经由a series of articles by Stephen Toub 记录,但微软最近更改了其博客引擎,旧的迁移博客文章的 URL 不再有效。
由于既不支持 .NET 4.0 也不支持 Parallel Extras,因此未修复链接。这些文章仍然可以在a different URL 下获得
“不再支持” 表示文档、库和知识也可能丢失。有些 7 年前做任务的人可能还记得当时使用的库和项目,但 7 年太长了。