2012-07-25 53 views
1

我讀了很多文章說多線程應用程序必須使用每個線程單獨的會話。也許我不明白鎖定是如何工作的,但是如果我在所有存儲庫方法中對會話鎖定,是否會使單個靜態會話線程不安全?鎖定存儲庫線程中的單個會話是否安全? (NHibernate)

,如:

public void SaveOrUpdate(T instance) 
{ 
    if (instance == null) return; 
    lock (_session) 
     using (ITransaction transaction = _session.BeginTransaction()) 
     { 
      lock (instance) 
      { 
       _session.SaveOrUpdate(instance); 
       transaction.Commit(); 
      } 
     } 
} 

編輯:

請考慮上下文/類型的應用我寫:

不支持多用戶,而不是典型的用戶交互,但自運行機器人對諸如財務數據和訂單更新等遠程事件做出反應,並基於此執行任務和保存。間歇性地,這可以創建每秒高達10次節省的羣集。通常它是每次都需要保存的同一個對象圖。另外,在啓動時,程序會將完整的數據庫加載到實體 - 對象圖中。所以它基本上只讀取一次,然後在運行時執行SaveOrUpdates。

+0

如果不是比multithreded爲什麼你需要鎖呢? – 2012-07-25 15:45:08

+0

@WiktorZychla他沒有說它不是多線程。他說這不是多用戶。 – 2012-07-25 15:45:33

+0

(我相信)每個遠程事件(來自套接字連接)創建它自己的線程。 – bretddog 2012-07-25 15:47:16

回答

4

鑑於應用程序通常會編輯同一個對象圖,也許將單個線程專用於將這些編輯應用於對象圖然後將它們保存到數據庫或可能是一組線程池會更有意義爲一個通用的編輯隊列提供服務,每個線程都有自己的(專用)會話,而不需要鎖定。查看生產者/消費者隊列(開始,看看here)。

事情是這樣的:

[Producer Threads] 
Edit Event -\    [Database Servicer Thread] 
Edit Event ------> Queue -> Dequeue and Apply to Session -> Database 
Edit Event -/ 

我會想象一個BlockingCollection<Action<Session>>將是這樣一個實現一個很好的起點。

這裏有一個粗略的例子(注意,這顯然是未經測試):

// Assuming you have a work queue defined as 
public static BlockingCollection<Action<Session>> myWorkQueue = new BlockingCollection<Action<Session>>(); 

// and your eventargs looks something like this 
public class MyObjectUpdatedEventArgs : EventArgs { 
    public MyObject MyObject { get; set; } 
} 

// And one of your event handlers 
public MyObjectWasChangedEventHandler(object sender, MyObjectUpdatedEventArgs e) { 
    myWorkQueue.Add(s=>SaveOrUpdate(e.MyObject)); 
} 

// Then a thread in a constant loop processing these items could work: 
public void ProcessWorkQueue() { 
    var mySession = mySessionFactory.CreateSession(); 
    while (true) { 
     var nextWork = myWorkQueue.Take(); 
     nextWork(mySession); 
    } 
} 

// And to run the above: 
var dbUpdateThread = new Thread(ProcessWorkQueue); 
dbUpdateThread.IsBackground = true; 
dbUpdateThread.Start(); 
+0

要將所有更新推送到單個線程,即使是主線程,聽起來也是一個好主意。我想這也會消除TransientObjectException,我剛剛發佈了其他問題。但是,這是如何完成的?我的API客戶端對象具有30個帶有自定義EventArgs的事件。在特定線程上進行這些觸發的原理是什麼? – bretddog 2012-07-25 17:45:04

+0

您可以構造一個在Session上作用並捕獲EventArgs的lambda表達式。我會舉一個例子。 – 2012-07-25 17:46:43

+0

這很有趣。我仍然試圖將它與我的代碼聯繫起來,因爲我需要做更多的事情來保存EventArgs,比如更新幾個對象並保存更大的對象圖。而且每個事件的邏輯都不相同,所以不確定是否可以將其作爲單個隊列。否則,我有一個想法;如果我只是讓所有事件處理程序獲取相同的虛擬鎖,那麼他們是不是隻需要等待對方,創建自己的隊列呢? – bretddog 2012-07-25 21:32:53

2

至少有兩個缺點:

  1. 您顯著降低了性能。在繁忙的網絡服務器上使用它就像在電影院外聚集人羣,但讓人們通過一個人的入口進入。

  2. 會話有其內部身份映射(緩存)。每個應用程序的單個會話意味着內存消耗隨着用戶訪問數據庫中的不同數據而增加。最終,你甚至可以結束內存中的整個數據庫,這當然不起作用。這需要調用一個方法來不時放置第一級緩存。但是,沒有好時機放棄緩存。你不能在請求開始時插入,因爲其他併發會話可能會因此而受到影響。

我相信人們會增加其他缺點。

+0

謝謝!請參閱我的編輯。根據我的觀察,創建一個會話需要0.5秒(使用SQLite),所以使用單個會話是我嘗試提高性能的一種方式。無論如何,我確實將完整的數據庫加載到內存中。 – bretddog 2012-07-25 15:44:31

+2

創建一個會話應該需要20ms。也許你沒有創建會話,而是重新創建了ISessionFactory(當然這不是必需的)? – 2012-07-25 15:48:12

+0

我將不得不檢查,如果它應該是那麼快..但我也相信線程的多個會話保存非常迅速會創建大量過時狀態異常..(?) – bretddog 2012-07-25 15:50:58