我有一个MVVM应用程序,它调用数据服务来获取要绑定的数据。数据服务通过Entity Framework 6访问SQL Server Compact(v4.0)数据库。
数据(当前)需要几秒钟才能加载,当同步调用时(这并不奇怪)会阻止GUI线程。我假设大部分都是IO绑定的,所以我添加了一个等效的Async方法来以异步方式执行数据加载,以允许GUI保持响应。但是,当我使用EF Async方法时,它似乎没有任何区别,GUI线程仍会阻塞大致相同的时间。
据我所知,使用Async调用不会将工作转移到其他线程,但是我假设此活动的大部分都是IO绑定的。因此,使用EF Async方法应该允许调用(GUI)线程继续做其他工作,但它没有任何区别。
例如,如果我编写LoadData方法以使用EF同步加载方法,则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显然不会阻塞并且整个时间都在响应。
我已经阅读了很多相关文章,包括帖子here和here以及Stephen Cleary关于什么时候不(here)以及何时({{}}的博客文章{3}})使用Task.Run。我知道上面的上一个例子仍然是阻塞的,它只是阻塞了一个线程池线程。我真正理解的是,为什么在访问EF异步方法时它似乎没有提供任何好处?
当IO活动正在进行时,是否还有足够的CPU绑定工作来导致阻塞?响应时间肯定要超过50毫秒,接近2或3秒才能运行所有查询,所以这种类型的活动可以开始证明使用Task.Run是合理的吗?
或者,我是否错误地编写了Async方法,为什么它仍然阻塞?
我也想知道EF的SQL Compact提供程序的异步方法是否出于某些原因而退回到标准的同步调用?
答案 0 :(得分:2)
SQL Server Compact ADO.NET提供程序不提供任何异步API。但是加载数据永远不会花费几秒,除非您获取1000行。启动应用程序时初始化dbContext.connection对象,并在应用程序的生命周期内保留空和未使用状态。第一次还有dbContext初始化的代价。