我在.NET 4.0上使用C#,并开始用BackgroundWorker
替换一些嵌套的Task<T>
设置。
&#34;嵌套&#34;就是这种形式:
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
- 虽然执行的任务不使用令牌)看起来像滥用CancellationToken
机制。真的有多糟糕?什么是正确的,&#34;惯用的&#34;根据流程中确定的信息取消连续链的方法?
(这个代码在外面有预期的效果,但我认为这是解决使用现有工具的任务的错误方法。我不是在寻找对我的解决方案的批评&#34;但这是正确的方法。这就是为什么我把它放在SO而不是Code Review上。)
答案 0 :(得分:1)
使用continuation,C#还提供async
方法功能。在这个方案中,您的代码看起来像这样:
async Task<bool> Things() { ... }
async Task MoreThings() { ... }
async Task RunStuff()
{
if (await Things())
{
await MoreThings();
}
}
具体细节取决于您的实施细节。这里重要的是通过async
和await
,C#将自动生成一个状态机,它将抽象所有乏味的东西从处理延续中抽象出来,以便于组织它们。< / p>
编辑:我发现你的实际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());
}
});
}
无论如何都是这样的。