EF数据上下文 - 异步/等待和放大器;多线程 [英] EF Data Context - Async/Await & Multithreading

查看:276
本文介绍了EF数据上下文 - 异步/等待和放大器;多线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我经常使用异步/计谋,以确保ASP.NET MVC的Web API线程不会被长时间运行的I / O和网络操作,特别是数据库调用。

I frequently use async/await to ensure ASP.NET MVC Web API threads are not blocked by longer-running I/O and network operations, specifically database calls.

System.Data.Entity的命名空间提供各种帮助扩展在这里,如 FirstOrDefaultAsync ContainsAsync CountAsync 的,等等。

The System.Data.Entity namespace provides a variety of helper extensions here, such as FirstOrDefaultAsync, ContainsAsync, CountAsync and so forth.

不过,由于数据的上下文不是线程安全的,这意味着下面的code是有问题的:

However, since data contexts are not thread safe, this means that the following code is problematic:

var dbContext = new DbContext();
var something = await dbContext.someEntities.FirstOrDefaultAsync(e => e.Id == 1);
var morething = await dbContext.someEntities.FirstOrDefaultAsync(e => e.Id == 2);

其实,我的有时的看到例外,如:

System.InvalidOperationException:   该连接未关闭。该连接的当前状态是开放的。

System.InvalidOperationException: The connection was not closed. The connection's current state is open.

是正确的模式,然后使用一个单独的使用(新的DbContext ...)块的每个的异步的数据库调用?是不是可能更有利,只是执行同步,然后呢?

Is the correct pattern then to use a separate using(new DbContext...) block for each asynchronous call to the database? Is it potentially more beneficial to just execute synchronous then instead?

推荐答案

我们这里有一个僵持的局面。 AspNetSynchronizationContext ,这是负责的ASP.NET Web API执行环境的线程模型,在等待将在同一个线程的地方。这样做的整体思路是从线程池使ASP.NET应用程序更具伸缩性,因此较少线程被阻塞与挂起的同步操作。

We have a stalemate situation here. AspNetSynchronizationContext, which is responsible for the threading model of an ASP.NET Web API execution environment, does not guarantee that asynchronous continuation after await will take place on the same thread. The whole idea of this is to make ASP.NET apps more scalable, so less threads from ThreadPool are blocked with pending synchronous operations.

不过,的DataContext 不是线程安全的,所以它不应该被用在一个线程切换可能会潜在地occurr横渡的DataContext API调用。一个单独的使用每个异步调用构造将不会帮助,或者:

However, DataContext is not thread-safe, so it shouldn't be used where a thread switch may potentially occurr across DataContext API calls. A separate using construct per asynchronous call will not help, either:

var something;
using (var dataContext = new DataContext())
{
    something = await dataContext.someEntities.FirstOrDefaultAsync(e => e.Id == 1);
}

这是因为 DataContext.Dispose 可能从一个对象最初创建在不同的线程中执行,这是不是的DataContext 所期望的。

That's because DataContext.Dispose might be executed on a different thread from the one the object was originally created on, and this is not something DataContext would expect.

如果你喜欢坚持使用的DataContext API,称这是同步似乎是唯一可行的选择。我不知道这种说法应该扩展到整个EF API,但我想用的DataContext API创建的子对象很可能不是线程安全的,无论是。因此,在ASP.NET的使用范围应仅限于两间相邻的等待电话

If you like to stick with the DataContext API, calling it synchronously appears to be the only feasible option. I'm not sure if that statement should be extended to the whole EF API, but I suppose any child objects created with DataContext API are probably not thread-safe, either. Thus, in ASP.NET their using scope should be limited to that of between two adjacent await calls.

这可能是诱人卸载一堆同步的DataContext 调用一个单独的线程与等待Task.Run(()=> { / *做DataContext的东西在这里* /})。然而,这会是已知反模式,特别是在ASP.NET的上下文中,它可能只伤害的性能和可扩展性,因为它不会减少到满足该请求的所需线程的数目。

It might be tempting to offload a bunch of synchronous DataContext calls to a separate thread with await Task.Run(() => { /* do DataContext stuff here */ }). However, that'd be a known anti-pattern, especially in the context of ASP.NET where it might only hurt performance and scalability, as it would not reduce the number of threads required to fulfill the request.

不幸的是,当ASP.NET的异步架构是伟大的,但它仍然是有一些既定的API和模式(例如,这里是的类似的案例)。 这是特别难过,因为我们没有并发API访问这里处理,即不超过一个线程试图访问同一时间的DataContext 对象。

Unfortunately, while the asynchronous architecture of ASP.NET is great, it remains being incompatible with some established APIs and patterns (e.g., here is a similar case). That's especially sad, because we're not dealing with concurrent API access here, i.e. no more than one thread is trying to access a DataContext object at the same time.

希望,微软将针对在框架的未来版本。

Hopefully, Microsoft will address that in the future versions of the Framework.

[更新] 在大规模不过,这也许可以卸载EF逻辑到一个单独的进程(运行的WCF服务),这将提供一个线程安全的异步API来的ASP.NET客户端逻辑。这样的过程可以用自定义同步环境作为一个事件的机器,类似node.js中进行编排它甚至可以运行一个池的Node.js般的公寓,每间公寓都保持了EF对象的线程关联。这将允许仍然从异步EF API中获益。

[UPDATE] On a large scale though, it might be possible to offload the EF logic to a separate process (run as a WCF service) which would provide a thread-safe async API to the ASP.NET client logic. Such process can be orchestrated with a custom synchronization context as an event machine, similar to Node.js. It may even run a pool of Node.js-like apartments, each apartment maintaining the thread affinity for EF objects. That would allow to still benefit from the async EF API.

[更新] 以下是一些尝试以找到一个解决这个问题。

[UPDATE] Here is some attempt to find a solution to this problem.

这篇关于EF数据上下文 - 异步/等待和放大器;多线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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