2010-03-21 26 views
18

提高服务器应用程序可伸缩性的一种方法是异步运行IO绑定操作(读取文件,套接字,Web请求,数据库请求等)。这并不意味着在ThreadPool中运行它们,它只会在执行操作时阻塞线程。正确的方法是使用异步API(BeginRead,BeginGetResponse,BeginExecuteReader等)。这个问题在CLR vi C#书中有很好的描述。如何异步运行NHibenate查询?

这是一些关于asynchronous queries in Linq to SQL的文章。

是否有异步执行Nhibernate查询的方法? Linq对NHibernate怎么样?

谢谢 安德烈

+0

“异步”支持正在为NHibernate 4.2.0和5.0.0提供支持。 https://nhibernate.jira.com/browse/NH-3971 – 2017-09-11 17:53:38

回答

9

不幸的是,没有。 NHibernate没有以L2S的方式公开命令执行的内部实现。

您将不得不使用线程池或为NH添加补丁以添加异步查询支持。这将是非常受欢迎的社区,并会作出一个很好的练习(但它不是微不足道的)

+0

期货+ 1项任务对所有数据库来说都很好吗?某些sql数据库不支持MARS(这是多个异步调用所需的)(并且确实需要?) – Firo 2013-09-05 13:39:12

+0

期货不会开始执行。它们只是多查询的封装。 – 2013-09-05 18:00:53

+0

我知道这一点。这就是为什么我写了“+任务”。看到我的回答下面我的意思 – Firo 2013-09-06 05:40:34

12

请注意,异步数据库调用并不意味着更好的整体可伸缩性本身。我建议阅读文章“Should my database calls be Asynchronous?”进行深入分析。下面是这篇文章报价:

一个尊重DB /网页设计师竟然 至于说:
对于使用异步操作 数据库 应用减少在Web服务器上阻塞的线程 数几乎总是浪费时间。小型网络 服务器可以轻松处理更多 同时阻止请求,而不是 您的数据库后端可以同时处理 。相反,请确保您的 服务电话是在 数据库便宜,并限制 并发执行的请求,你已经测试正常工作 ,最大限度地提高整体 交易吞吐量 数数。

+8

这个说法截至2012年8月(在.net 4.5 rtm之后)变得无效。现在异步代码的成本显着降低,以异步方式编写数据库操作绝对没问题。但在这一刻,NHibernate仍然不支持。希望这将是很快失效:) – 2012-09-18 22:09:34

+9

@BorisBucha .NET 4.5与此无关。仅仅因为您可以更轻松地在C#代码中表示异步代码并不意味着Web服务器和数据库会自动扩展。 – 2012-09-19 02:21:08

+6

我相信它。确定你不能通过以异步方式调用你的数据库,但是你的web服务器更容易垂直扩展。少线程意味着更少的内存。如果我可以大大简化话题,我可以说通过利用异步性,你总是赢。 性能在最坏的情况下保持不变。在最好的情况下,你可以通过禁止io绑定任务占用Threadpool来显着改善它。 而开发成本几乎没有,所以我没有看到任何理由不以异步方式从.net4.5开始。 – 2012-09-19 07:54:51

1

虽然NH中仍然不支持异步查询,但您仍然可以部分克服从请求线程运行(长时间运行)数据库调用的一些不良影响。

你想要的是在短时间运行和长时间运行之间分割线程池。当然,这对于Threadpool和TPL的实际实现来说是不可能的,但是通过编写自己的Producer/Consumer队列以及等待项目和自定义的协作性,您可以帮助自己完成很多工作。

请看看例子,我已经把:https://gist.github.com/3746240

代码被复制/从伟大的书“C#5.0果壳中的权威参考”粘贴由约瑟夫阿尔巴哈利和Ben阿尔巴哈利与修改由我做导致调度程序为项目处理创建专用工作线程。

+0

仅供参考,请记住,Threadpool(以及后续的“等待Task.StartNew(...)”)具有自动增长功能,即使在高负载情况下,最终可能会产生许多专用线程,这可能是更理想的解决方案。老实说,我没有做任何性能比较我提出的解决方案与普通老式线程池相比,但我相信它可能有助于在某些情况下(如果你测量慢自动增长本身造成的perf泄漏:)) – 2012-09-18 23:03:03

11

多个异步调用可以用期货来改写

var footask = QueryFooAsync(); 
var bartask = QueryBarAsync(); 
var baztask = QueryBazAsync(); 

var foos = await footask; 
var bars = await bartask; 
var baz = await baztask; 

// do something with foos, bars, baz 

可以

var foos = session.Query<Foo>().....ToFuture(); 
var bars = session.Query<Bar>().....ToFuture(); 
var baz = session.Query<Bazes>().....ToFutureValue(); 

await Task.Factory.StartNew(() => var ignored = baz.Value) // await the results 

// do something with foos, bars, baz 

这甚至已超过异步代码的益处,即往返时间只支付一次,而不是3次更换。

+1

这不是真正的异步操作我不会建议这样做,因为现在你只是创建另一个线程。如果要装载cpu绑定作业来分离线程,启动新任务可能会有所帮助,但db调用是I/O操作,并且对于异步API而言,它最适合。 – 2017-02-07 04:34:23

+0

@AndzejMaciusovic你是对的,它不是真正的异步释放线程,但它阻止线程更短(1往返而不是3),并可以减轻数据库的负载,因为它只处理1请求(有3个查询) 。看到Mauricio Scheffer回答为什么保存CPU可能没有多大帮助。 – Firo 2017-02-07 19:31:29