我有一个MVVM应用程序,该应用程序调用数据服务以获取一些要绑定的数据。数据服务通过实体框架6访问SQL Server Compact(v4.0)数据库。
数据(当前)需要几秒钟的时间加载,并且在同步调用时,这(不足为奇)会阻塞GUI线程。我假设其中大多数都是受IO约束的,所以我添加了等效的Async方法以异步方式执行数据加载,以使GUI保持响应。但是,当我使用EF Async方法时,似乎没有什么区别,GUI线程仍会阻塞大约相同的时间。
我知道使用Async调用不会将工作移到另一个线程,但是我已经假定此活动的大部分是IO绑定的。因此,使用EF异步方法应允许调用(GUI)线程继续执行其他工作,但没有任何区别。
例如,如果我编写LoadData方法以使用EF同步Load方法,则GUI会阻塞直到数据加载完毕:
public ObservableCollection<Data> GetData()
{
//Do IO bound activity...
Context.DataTable1.Include(...).Load();
//for demo purposes, just return some data
return Context.DataTable1.Local; //(ObservableCollection<Data>)
}
加载数据的Async方法如下所示:
public async Task<ObservableCollection<Data>> GetDataAsync()
{
//Do IO bound activity...
var task = Context.DataTable1.Include(...).LoadAsync();
await task;
//for demo purposes, just return some data
return Context.DataTable1.Local; //(ObservableCollection<Data>)
}
出乎意料的是(对我而言),我得到了相同的结果,并且它在大致相同的时间内阻塞了调用线程(我在上面放了一块秒表)。
我开始认为,除了数据库IO绑定工作外,可能还有一些最小数量的CPU绑定活动正在引起阻塞。所以我最终尝试使用Task.Run()在后台线程上执行工作:
public async Task<ObservableCollection<Data>> GetDataAsync()
{
//Do IO bound activity...
Task<ObservableCollection<Competition>> task = Task.Run(async () =>
{
//Do IO bound activity...
var task = Context.DataTable1.Include(...).LoadAsync();
await task;
//for demo purposes, just return some data
return Context.DataTable1.Local; //(ObservableCollection<Data>)
});
var result = await task;
return result;
}
这样,GUI显然不会被阻塞,并且在整个过程中都可以响应。
我四周看了很多文章这一点,包括职位在这里和这里和博客文章由斯蒂芬·克利里约时,不要(在这里),何时(这里)使用Task.Run。我知道上面的最后一个示例仍在阻塞,它只是在阻塞线程池线程。我真正不了解的是,为什么在访问EF异步方法时它似乎没有提供任何好处?
可能是因为正在进行IO活动,但同时也有足够的CPU绑定工作导致阻塞?当然,响应所需的时间明显超过50毫秒,而运行所有查询的时间将接近2或3秒,因此这种类型的活动可以开始证明使用Task.Run是合理的。
还是我写的Async方法不正确,为什么它仍然阻塞?
我还想知道是否可能由于某种原因而使EF的SQL Compact提供程序的Async方法回退到标准的同步调用?
SQL Server Compact ADO.NET提供程序不提供任何异步API。但是,除非要获取1000的行,否则加载数据的时间绝对不要花几秒钟。启动应用程序时初始化dbContext.connection对象,并在应用程序的生命周期内保留为空且未使用。第一次初始化dbContext也有代价。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句