我正在调用Task.Run(()=> DoSomething())。Result,它导致UI冻结,并且由于使用“ .Result”而发生。我需要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;
}
答案 0 :(得分:2)
方法StartSomething()
对我来说没有意义。它开始一个新的Task
,然后同步地等待此任务的结果(.Result
),实际上是无用的-它几乎是 [*] < / strong>与直接调用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;
}
虽然一直使用 async 解决方案(如上所示)是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 Toub的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;
}
嵌套循环是一种非常先进的技术(我实际上从未使用过),除非有必要,否则我不建议您使用它。
[*] 在异常处理,执行线程等方面存在差异,但与该问题无关。