以下是我最近用来说明使用异步解决问题的不同之处和各种问题的代码片段序列。
假设您的基于GUI的应用程序中有一些事件处理程序需要很长时间,因此您希望使其异步化。以下是您开始使用的同步逻辑:
while (true) {
string result = LoadNextItem().Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
break;
}
}
LoadNextItem
返回一个Task,最终会产生一些你想要检查的结果。如果当前结果是你要找的那个,你就在UI上更新一些计数器的值,并从方法中返回。否则,你将继续处理来自LoadNextItem
的更多项。
异步版本的第一个想法:只需使用continuations!暂时先忽略循环部分。我的意思是,可能会出什么问题呢?
return LoadNextItem().ContinueWith(t => {
string result = t.Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
}
});
太好了,现在我们有了一个不会阻塞的方法!但它可能会崩溃。任何对UI控件的更新都应该在UI线程上进行,所以您需要考虑到这一点。幸运的是,有一个选项可以指定如何安排续集,并且有一个默认选项:
return LoadNextItem().ContinueWith(t => {
string result = t.Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
}
},
TaskScheduler.FromCurrentSynchronizationContext());
太好了,现在我们有了一个不会崩溃的方法!它会默默地失败。Continuations是单独的任务,其状态与前置任务无关。因此,即使LoadNextItem
出现故障,调用者也只会看到一个已成功完成的任务。好的,那么如果有异常,就将其传递:
return LoadNextItem().ContinueWith(t => {
if (t.Exception != null) {
throw t.Exception.InnerException;
}
string result = t.Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
}
},
TaskScheduler.FromCurrentSynchronizationContext());
太好了,现在这个确实有效。对于单个项目而言是如此。现在,如何循环呢?事实证明,与原始同步版本逻辑等效的解决方案将类似于以下内容:
Task AsyncLoop() {
return AsyncLoopTask().ContinueWith(t =>
Counter.Value = t.Result,
TaskScheduler.FromCurrentSynchronizationContext());
}
Task<int> AsyncLoopTask() {
var tcs = new TaskCompletionSource<int>();
DoIteration(tcs);
return tcs.Task;
}
void DoIteration(TaskCompletionSource<int> tcs) {
LoadNextItem().ContinueWith(t => {
if (t.Exception != null) {
tcs.TrySetException(t.Exception.InnerException);
} else if (t.Result.Contains("target")) {
tcs.TrySetResult(t.Result.Length);
} else {
DoIteration(tcs);
}});
}
或者,您可以使用 async
来完成同样的操作:
async Task AsyncLoop() {
while (true) {
string result = await LoadNextItem();
if (result.Contains("target")) {
Counter.Value = result.Length;
break;
}
}
}
那现在好多了,是吧?
getWebPage
方法不能同时在两个代码中使用。在第一个代码中,它具有Task<string>
返回类型,而在第二个代码中,它具有string
返回类型。因此,基本上你的代码无法编译。- 如果要精确的话。 - Royi Namir