我在 .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()
的延续;但是,这不允许我决定是否在特定情况下实际从控制流运行延续。
A - 根据我的理解非常不干净 - 解决方法是这样的:
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
- 尽管执行的任务不使用 token )看起来更像是滥用CancellationToken
机制。这到底有多糟糕?根据流中确定的信息取消延续链的正确、“惯用”方法是什么?
(外部代码具有预期效果,但我认为就使用现有工具而言,这是解决任务的错误方法。我不是在寻找对我的“解决方案”的批评,而是在寻找正确的方法去做。这就是为什么我把它放在 SO 而不是代码审查上。)
最佳答案
对于延续,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/