我已经与 Task
合作过类型。一切都很好,同时 Task
什么都不回。例如:
XAML:
<Button Name="_button"
Click="ButtonBase_OnClick">
Click
</Button>
代码隐藏:
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
_button.IsEnabled = false;
Task.Factory.StartNew(() =>
{
Thread.Sleep(5*1000);
Dispatcher.Invoke(new Action(() => _button.IsEnabled = true));
});
}
这工作正常。但我想
Task
返回一些值,例如 Boolean
.所以我需要使用 Task<Boolean>
:private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
_button.IsEnabled = false;
var task = Task<Boolean>.Factory.StartNew(() =>
{
Thread.Sleep(5*1000);
return true;
});
if (task.Result)
_button.IsEnabled = true;
}
在这里,我们遇到了 UI 阻塞的问题。 UI 线程被锁定,直到任务返回结果。
_button.IsEnabled = false;
因此,上面的字符串完全被忽略。
我在
.Net 4.0
,所以我不能使用 async/await
方法。这个问题真的让我恶心……有解决方案吗?
最佳答案
您的主线程被阻塞,因为调用 Task.Result
等到Task
已经完成。相反,您可以使用 Task.ContinueWith()
访问 Task.Result
后 Task
已经完成。调用 TaskScheduler.FromCurrentSynchronizationContext()
导致继续在主 UI 线程上运行(因此您可以安全地访问 _button
)。
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
_button.IsEnabled = false;
Task<Boolean>.Factory.StartNew(() =>
{
Thread.Sleep(5*1000);
return true;
}).ContinueWith(t=>
{
if (t.Result)
_button.IsEnabled = true;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
更新
如果您使用的是 C# 5,则可以改用 async/await。
private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
_button.IsEnabled = false;
var result = await Task.Run(() =>
{
Thread.Sleep(5*1000);
return true;
});
_button.IsEnabled = result;
}
关于WPF 异步 Task<T> 阻塞 UI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23934236/