2010-03-21 79 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