我无法按预期运行ComputeListener函数。
我希望甚至在 Compute()开始之前就可以在UI上立即更新StatusBlock.Text
,但是我无法使其正常工作。
我知道这个问题已经在StackOverflow的其他地方问过了,但是我所见的任何解决方案都不适合我。
在此过程中,我已经处理了几个问题,但是当前的问题是,直到 ComputeListener()完全完成其执行并且所有其他功能都被锁定之后,才可以进行任何更改。 / p>
如果Compute()花费的时间太长,我还希望能够单击关闭按钮来停止应用程序,但这也不会发生。
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public void ComputeListener(object sender, RoutedEventArgs args)
{
var t = Task.Run(() => Dispatcher.BeginInvoke(new Action(() => { StatusBlock.Text = "Status: Processing"; })));
t.Wait();
Task.Run(() => Dispatcher.BeginInvoke(new Action(() => { Compute(); })));
}
private void Compute()
{
//code here
}
答案 0 :(得分:1)
看起来您已经正确地识别出,在UI线程上执行Compute()
将阻止StatusBlock.Text
的更改按需要反映在UI中,因此应使用Task
。 / p>
但是,在尝试从辅助线程更新UI时,您会遇到跨线程异常,因此您已使用Window.Dispatcher
返回了UI线程。
如Icepickle所建议-您想要在这里async
/ await
。我认为以下方法应该有效:
public async void ComputeListener(object sender, RoutedEventArgs args)
{
StatusBlock.Text = "Status: Processing";
await Task.Run(() => Compute());
}
private void Compute()
{
//code here
}
甚至更好:
public async void ComputeListener(object sender, RoutedEventArgs args)
{
StatusBlock.Text = "Status: Processing";
await ComputeAsync();
}
private async Task ComputeAsync()
{
//async code here
}
注意,如果您的Compute()
的实现也需要访问UI,您仍然会遇到问题-这是您的业务逻辑(即计算)的代码味道应该独立于WPF前端。
我建议您转向MVVM方法-该Xamarin.Forms documentation很好地解释了一般概念,可以以几乎相同的方式将其应用于WPF
答案 1 :(得分:0)
您应该使用异步调度程序成员Dispatcher.InvokeAsync
,而不是将其包装到Task
中。
Task.Run
始终可以异步执行。 Task.Wait()
与强制Task
同步执行的想法相矛盾,因为它阻塞线程,直到Task
完成,取消或出现故障。阅读Microsoft Docs的注释:Task.Wait
。与Task.Run
结合使用时,引入死锁的机会很高。因此,如果需要加入多个Task.Wait
实例,请始终避免使用Task.WaitAny
并使用异步和等待版本Task.WaitAll
和Task
。
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public async void ComputeListener(object sender, RoutedEventArgs args)
{
// Don't block while waiting for Dispatcher to execute the action
await Dispatcher.InvokeAsync(() => StatusBlock.Text = "Status: Processing");
// Executes asynchronously on a background thread (thread pool thread).
// Use for CPU bound operations
await ComputeOnNewBackgroundThreadAsync();
// Executes asynchronously on the current thread.
// Use for lightweight async operations
Task task = new Task(ComputeOnCurrentUiThread);
task.Start();
await task;
// Waiting for tasks example
var tasks = new List<Task>();
Task currentThreadTask = new Task(ComputeOnCurrentUiThread);
currentThreadTask.Start();
tasks.Add(currentThreadTask);
Task backgroundThreadTask = ComputeOnNewBackgroundThreadAsync();
tasks.Add(backgroundThreadTask);
// Wait asynchronously for both Task instances to complete
await Task.WhenAll(tasks);
}
private async Task ComputeOnNewBackgroundThreadAsync()
{
await Task.Run(
() =>
{
//code here
});
}
private void ComputeOnCurrentUiThread()
{
//code here
}
}