Wpf 异步等待似乎使用UI线程
在视图模型中,我使用工厂:Wpf 异步等待似乎使用UI线程,wpf,async-await,Wpf,Async Await,在视图模型中,我使用工厂: private async Task<BaseData> InitializeAsync() { await InstancesAsync(); await ProjectsAsync(); await AdminAsync(); return this; } public static async Task<BaseData> CreateAsync() { var ret = new BaseData
private async Task<BaseData> InitializeAsync()
{
await InstancesAsync();
await ProjectsAsync();
await AdminAsync();
return this;
}
public static async Task<BaseData> CreateAsync()
{
var ret = new BaseData();
return await ret.InitializeAsync();
}
虽然它可以工作,但我感到相当不舒服,因为UI线程到处都在使用,而且在等待完成的回调之后也是如此。我错过了什么
另外,我不知道如何使用工厂模式来处理DataContext
,因为如果没有上面的Invoke
,我会得到一个错误,即不同的线程拥有该对象
编辑:利用克利里先生的想法,我得到:
Loaded += async (object sender, RoutedEventArgs e) =>
{ DataContext = await BaseData.CreateAsync(); };
public static Task<BaseData> CreateAsync()
{
var ret = new BaseData();
return ret.InitializeAsync();
}
private async Task<BaseData> InitializeAsync()
{
// UI thread here
await InstancesAsync().ConfigureAwait(false);
// thread 'a' here
await ProjectsAsync().ConfigureAwait(false);
// thread 'a' sometimes 'b' here
await AdminAsync().ConfigureAwait(false);
// thread 'a' or 'b' here
return this;
}
Loaded+=async(对象发送方,RoutedEventArgs e)=>
{DataContext=wait BaseData.CreateAsync();};
公共静态任务CreateAsync()
{
var ret=新的BaseData();
返回ret.InitializeAsync();
}
专用异步任务InitializeAsync()
{
//UI线程在这里
await InstanceAsync().ConfigureAwait(false);
//在这里穿“a”
wait ProjectsAsync().configurewait(false);
//在此处线程“a”有时为“b”
await AdminAsync().ConfigureAwait(false);
//在此处插入“a”或“b”
归还这个;
}
这可以正常工作,但我无法理解ConfigureAwait(false)
是如何工作的。在方法
instanceAsync()
中,我有一个等待的任务:var instances=wait TaskEx.Run(new Func(()=>Agent.GetInstances())代码>
在等待repsonse之后,我返回UI线程-我从未预料到会发生这种情况
请注意,ProjectsAsync()
和adminancy()
的行为相同,尽管它们是从工作线程(或后台线程)开始的
我认为ConfigureAwait(true)
具有在调用线程(在我的例子中是UI线程)中返回的效果。我对此进行了测试,结果就是这样。
为什么我在ConfigureAwait(false)
中也会看到这一点:由于嵌套的await,请参见注释。我发现将ViewModel视为具有UI线程关联性最有用。将其视为逻辑用户界面,即使它不是实际的用户界面。因此,ViewModel类上的所有属性和可观察集合更新都应该在UI线程上完成
在async
方法中,如果不需要返回UI线程,则可以使用ConfigureAwait(false)
避免在UI线程上恢复。例如,如果您的各种初始化方法是独立的,则可以执行以下操作:
private async Task<BaseData> InitializeAsync()
{
// Start all methods on the UI thread.
var instancesTask = InstancesAsync();
var projectsTask = ProjectsAsync();
var adminTask = AdminAsync();
// Await for them all to complete, and resume this method on a background thread.
await Task.WhenAll(instancesTask, projectsTask, adminTask).ConfigureAwait(false);
return this;
}
最后,您应该极力避免使用调度程序。您加载的事件可以简化为:
Loaded += async ()
{
DataContext = await BasisGegevens.CreateAsync();
};
您显示的代码中加载的事件的来源是什么?如果这是一个常规事件,则应在UI线程上激发它。是否在单独的线程上创建视图?这可能是因为您正在非UI池线程上调用Agent.GetInstances()
?加载的事件是一个常规的FrameworkElement.Loaded事件。我打算在UI线程上创建视图,这显然是错误的。Agent.GetInstances()在等待的任务中运行,我希望它在非UI池线程上运行,但事实并非如此。TaskEx.Run
确实使用单独的池线程来运行Func
委托,这就是执行Agent.GetInstances()
的地方。我建议您遵循Stephen的答案,并确保WPF UI元素和ViewModel对象都是在同一个-UI-线程上创建的。添加一些日志(System.Threading.Thread.CurrentThread.ManagedThreadId
)以查看您所在的线程和位置。ConfigureAwait(false)
只影响特定的await
,它对它“内部”的await
没有影响。@svick:当然,谢谢,我还检查了它。我从您位于“”的工厂模式中派生了return await
。任务。所有时:任务必须按确定的顺序运行。加载的委托我必须以异步(对象发送方,RoutedEventArgs e)
的形式编写,才能编译。
private async Task<BaseData> InitializeAsync()
{
// Start all methods on the UI thread.
var instancesTask = InstancesAsync();
var projectsTask = ProjectsAsync();
var adminTask = AdminAsync();
// Await for them all to complete, and resume this method on a background thread.
await Task.WhenAll(instancesTask, projectsTask, adminTask).ConfigureAwait(false);
return this;
}
public static Task<BaseData> CreateAsync()
{
var ret = new BaseData();
return ret.InitializeAsync();
}
Loaded += async ()
{
DataContext = await BasisGegevens.CreateAsync();
};