我正在.NET 4.0上的C#中工作,并已开始用BackgroundWorker
替换许多嵌套的Task<T>
设置。
“嵌套”具有以下形式:
var secondWorker = new BackgroundWorker();
secondWorker.DoWork += (sender, args) =>
{
MoreThings();
};
var firstWorker = new BackgroundWorker();
firstWorker.DoWork += (sender, args) =>
{
args.Result = this.Things();
};
firstWorker.RunWorkerCompleted += (sender, args) =>
{
var result = (bool)args.Result;
// possibly do things on UI
if (result) { secondWorker.RunWorkerAsync(); }
};
secondWorker
在这里扮演firstWorker
的回调的角色。据我了解,使用Task<T>
时的等效项是ContinueWith()
的延续。但是,这不允许我决定是否在特定情况下根据控制流实际运行延续。我认为这是非常不干净的-解决方法是:
var source = new CancellationTokenSource();
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() => { return this.Things(); })
.ContinueWith(t =>
{
// do things on UI
if (!t.Result) { source.Cancel(); }
}, CancellationToken.None, TaskContinuationOptions.NotOnFaulted, uiScheduler)
.ContinueWith(t => { this.MoreThings(); }, source.Token);
这种工作方式,但是从我所看到的所有示例中,都以这种形式(从延续链中访问
CancellationTokenSource
-尽管未使用令牌的任务)看起来像是滥用了机制。这到底有多糟?根据其流程内部确定的信息,取消延续链的正确的“惯用”方法是什么?(上面的代码具有预期的效果,但是我认为这是解决使用现有工具的任务的错误方法。我不是在批评我的“解决方案”,而是在寻求正确的解决方案这就是为什么我将其放在SO而不是Code Review上的原因。
最佳答案
通过继续,C#还提供了async
方法功能。在此方案中,您的代码将如下所示:
async Task<bool> Things() { ... }
async Task MoreThings() { ... }
async Task RunStuff()
{
if (await Things())
{
await MoreThings();
}
}
具体细节取决于您的实施细节。这里重要的是,通过
async
和await
,C#将自动生成一个状态机,该状态机将使所有乏味抽象出来,而无需处理连续性,从而使它们易于组织。编辑:我想到您的实际
Things()
和MoreThings()
方法,您可能不想实际转换为async
,因此您可以这样做:async Task RunStuff()
{
if (await Task.Run(() => Things()))
{
await Task.Run(() => MoreThings());
}
}
这会将您的特定方法包装在异步任务中。
编辑2:有人向我指出,我在这里忽略了4.5之前的限制,以下应该起作用:
void RunStuff()
{
Task.Factory.StartNew(() => Things()).ContinueWith(task =>
{
if (task.Result)
{
Task.Factory.StartNew(() => MoreThings());
}
});
}
反正就是这样。
关于c# - 从内部取消延续链,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26747792/