WPF Dispatcher 有一个方法 InvokeAsync,它接受
Action
或 Func<T>
和 DispatcherPriority
。
我需要通过
Func<Task<T>>
并且我需要在具有给定优先级的 WPF 调度程序上运行整个 Task<T>
。
示例:
我需要调用一个必须在调度程序线程上运行的异步函数
Foo
:
async Task<SomeResult> Foo()
{
// some view model modification that needs to run on UI thread
...
// calling some IO call on thread pool
await Task.Run(() => SomeIoCallAsync());
// more view model modifications
...
// calling something on thread pool
await Task.Run(() => HeavyComputation());
// more view model modifications
...
return someResult;
}
来自不在 UI 线程上运行的异步函数
Bar
:
async Task<SomeOtherResult> Bar()
{
...
TODO call Foo() on UI thread
...
}
注意:如果
Foo
不是异步函数而只是同步函数,则 Bar
可能如下所示:
async Task<SomeOtherResult> Bar()
{
...
await dispatcher.InvokeAsync(Foo, someDispatcherPriority);
...
}
如何在调度程序线程上以某种优先级调用异步函数?
假设您实际上需要从方法
SomeResult
返回的 Foo()
值,您有两种选择。致电 await InvokeAsync
或直接拨打 Invoke
。
在这两种情况下,
Foo
都在UI线程上启动,并且返回的Task<SomeResult>
在您调用调度程序的线程中等待:
Task<SomeResult> task = await dispatcher.InvokeAsync(Foo, someDispatcherPriority);
SomeResult result = await task;
或
SomeResult result = await dispatcher.Invoke(Foo, someDispatcherPriority);
这里是一个代码示例,展示了它是如何工作的。
status
是主窗口 XAML 中的 TextBlock。
class SomeResult
{
public int ThreadId { get; set; }
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
async Task<SomeResult> Foo()
{
status.Text = $"Foo started in thread {Environment.CurrentManagedThreadId}";
var id1 = await Task.Run(() =>
{
Thread.Sleep(2000);
return Environment.CurrentManagedThreadId;
});
status.Text = $"First Task ran in thread {id1}";
var id2 = await Task.Run(() =>
{
Thread.Sleep(2000);
return Environment.CurrentManagedThreadId;
});
status.Text = $"Second Task ran in thread {id2}";
return new SomeResult { ThreadId = Environment.CurrentManagedThreadId };
}
async void Window_Loaded(object sender, RoutedEventArgs e)
{
await Task.Run(async () =>
{
var id = Environment.CurrentManagedThreadId;
Dispatcher.Invoke(() => status.Text = $"Main Task started in thread {id}");
await Task.Delay(2000);
SomeResult result = await Dispatcher.Invoke(Foo, DispatcherPriority.Normal);
await Task.Delay(2000);
Dispatcher.Invoke(() => status.Text = $"Foo ended in thread {result.ThreadId}");
});
}
}