我正在调用Task.Run(() => DoSomething()).Result,这会导致UI冻结,这是因为我使用了".Result“。我需要结果,因为我想返回值。
我不希望方法StartSomething是异步的,因为我不想等待方法StartSomething。我希望等待发生在DoSomething()上。
因此,基本上,我需要一个异步方法来被同步方法调用,而不冻结UI。另外,我希望将异步方法的值返回到Button Click上的顶层。
该代码可以改进吗?还是有其他解决方案?
private TaskCompletionSource<bool> TaskCompletion = null;
private void Button_Click(object sender, RoutedEventArgs e)
{
bool k = StartSomething();
}
private bool StartSomething()
{
return Task.Run(() => DoSomething()).Result;
}
private async Task<bool> DoSomething()
{
TaskCompletion = new TaskCompletionSource<bool>();
await Task.WhenAny(TaskCompletion.Task, Task.Delay(3000));
MessageBox.Show("DoSomething");
return true;
}
发布于 2018-11-16 02:31:29
方法StartSomething()
对我来说没有意义。它启动一个新的Task
,然后同步地等待这个任务的结果(.Result
),这实际上是无用的-它几乎是*,就像直接调用DoSomething()
一样。而且,DoSomething()
已经是异步的,所以您不需要为此启动一个新的Task
。
看起来您根本不需要StartSomething()
方法。如果您使Button_Click
处理程序async
,那么您可以直接await DoSomething()
:
private TaskCompletionSource<bool> TaskCompletion = null;
private async void Button_Click(object sender, RoutedEventArgs e)
{
bool k = await DoSomething();
}
private async Task<bool> DoSomething()
{
TaskCompletion = new TaskCompletionSource<bool>();
await Task.WhenAny(TaskCompletion.Task, Task.Delay(3000));
MessageBox.Show("DoSomething");
return true;
}
编辑:
虽然使用异步一直向下的解决方案(如上面所示)是IMO的首选方法,但是如果您真的不能将调用代码更改为async
,我可以想到两种方法从同步方法调用async
方法而不阻塞UI。首先是手动设置一个连续任务,如下所示:
private void Button_Click(object sender, RoutedEventArgs e)
{
DoSomething().ContinueWith((task) =>
{
bool k = task.Result;
// use the result
},
// TaskScheduler argument is needed only if the continuation task
// must run on the UI thread (eg. because it access UI elements).
// Otherwise this argument can be omitted.
TaskScheduler.FromCurrentSynchronizationContext());
// Method can exit before DoSomething().Result becomes
// available, which keep UI responsive
}
因此,您可以基本地将同步方法(一个拆分,而不是每个await
)拆分成由.ContinueWith
链接的几个部分(延续、lambda方法)。这类似于await
在引擎盖下所做的工作。问题是,与await
不同(它生成漂亮而干净的代码),您的代码将满是这些连续的lambda。当您添加异常处理块、using
块等时,情况会变得更糟。
第二种方法是使用嵌套循环,例如。Stephen的WaitWithNestedMessageLoop扩展方法:
static T WaitWithNestedMessageLoop<T>(this Task<T> task)
{
var nested = new DispatcherFrame();
task.ContinueWith(_ => nested.Continue = false, TaskScheduler.Default);
Dispatcher.PushFrame(nested);
return task.Result;
}
嵌套循环是非常高级的技术(实际上我从未使用过它),除非您必须使用它,否则我不建议使用它。
*在异常处理、执行线程等方面存在差异,但这些与此问题无关。
https://stackoverflow.com/questions/53332038
复制相似问题