使用Entity Framework异步方法和SQL Server Compact阻止行为 [英] Blocking behaviour with Entity Framework Async methods and SQL Server Compact

查看:103
本文介绍了使用Entity Framework异步方法和SQL Server Compact阻止行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个MVVM应用程序调用数据服务来获取一些数据绑定。数据服务通过实体框架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()
{
//做IO绑定活动...
Context.DataTable1.Include(...)。Load();

//用于演示目的,只需返回一些数据
return Context.DataTable1.Local; //(ObservableCollection< Data>)
}

加载数据的Async方法像这样:

  public async任务< ObservableCollection< Data>> GetDataAsync()
{
//做IO绑定活动...
var task = Context.DataTable1.Include(...)。LoadAsync();
等待任务

//用于演示目的,只需返回一些数据
return Context.DataTable1.Local; //(ObservableCollection< Data>)
}

令人惊讶的是(对我来说)相同的结果,它阻止了大致相同的时间长度的调用线程(我在其上放了一个秒表)。



我开始认为除了数据库IO绑定工作时,可能会有一些最小量的CPU绑定活动导致阻塞。所以我终于用Task.Run()执行了后台线程的工作:

  public async任务< ObservableCollection< Data> ;> GetDataAsync()
{
//做IO绑定活动...
任务< ObservableCollection<竞争>> task = Task.Run(async()=>
{
//做IO绑定活动...
var task = Context.DataTable1.Include(...)。LoadAsync );
await task;

//为演示目的,只返回一些数据
return Context.DataTable1.Local; //(ObservableCollection< Data>)
});
var result = await task;
返回结果;
}

有了这个,GUI显然不会阻止并在整个时间内做出回应



我已经阅读了很多文章,包括帖子这里这里和Stephen Cleary关于何时不( here )以及何时(这里)使用Task.Run。我明白我上面的最后一个例子是阻塞,它只是阻塞一个线程池线程。我不明白的是,为什么访问EF异步方法似乎没有提供任何好处?



可能是在IO活动进行的同时,还有足够的CPU绑定工作来阻止?确实需要超过50ms的时间来响应,更接近2或3秒才能运行所有查询,所以这种类型的活动可以开始证明使用Task.Run?



或者,我写了Async方法不正确,为什么它仍然阻塞?



我也想知道EF的SQL Compact提供商的Async方法是否因为某些原因而退回到标准的同步电话?

解决方案

SQL Server Compact ADO.NET提供程序不提供任何Async API。但加载数据不应该花费几秒钟,除非你获取1000行。在启动应用程序时初始化一个dbContext.connection对象,并在应用程序生命周期内保持空闲和未使用。 dbContext的初始化成本也是第一次。


I have an MVVM app that calls a data service to get some data to bind. The data service accesses a SQL Server Compact (v4.0) database through Entity Framework 6.

The data (currently) takes a few seconds to be loaded and when called synchronously this (not surprisingly) blocks the GUI thread. Most of this I had assumed to be IO bound so I added an equivalent Async method to execute the data load in an asynchronous fashion to allow the GUI to remain responsive. However when I make use of the EF Async methods, it doesn't appear to make any difference, the GUI thread still blocks, for roughly the same amount of time.

I understand that using the Async calls will not move the work to a different thread, however I had assumed the majority of this activity to be IO bound. Hence using the EF Async methods should allowing the calling (GUI) thread to continue doing other work, but it is not making any difference.

For example if I write the LoadData method to use the EF synchronous Load method, then the GUI blocks until the data is loaded:

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>)
}

The Async method to load the data looks like this:

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>)
}

Surprisingly (for me) I get the same result and it blocks the calling thread for roughly the same length of time (I put a stopwatch on it).

I started thinking that in addition to the database IO bound work, there may be some minimum amount of CPU bound activity that is causing the blocking. So I finally tried executing the work on a background thread by using 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;
}

With this, the GUI obviously doesn't block and is responsive the entire time.

I've read many articles all around this, including posts here and here and blog posts by Stephen Cleary about when not to (here) and when to (here) use Task.Run. I understand that my last example above is still blocking, it is just blocking a threadpool thread. What I don't understand really is, why when accessing the EF async methods does it not appear to be providing any benefit?

Could it be that whilst there is IO activity going on, there is also sufficient CPU bound work to cause the blocking? It is certainly taking significantly longer than 50ms to respond, closer to 2 or 3 seconds to run all the queries, so this type of activity can start justifying the use of the Task.Run?

Or, have I written the Async method incorrectly, hence why it is still blocking?

I also wondered if perhaps the Async methods for the SQL Compact provider for EF were for some reason falling back to the standard synchronous calls?

解决方案

The SQL Server Compact ADO.NET provider does not provide any Async APIs. But loading data should never take a fewcseconds, unless you are fetching 1000s of rows. Initialize a dbContext.connection object when you start your app, and leave empty and unused for the lifetime of your app. There is also a cost of init of dbContext first time.

这篇关于使用Entity Framework异步方法和SQL Server Compact阻止行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆