2010-07-15 29 views
4

你有一些很好的建議在多線程程序中使用EF嗎?在多線程程序中使用EF的好建議?

我有2層:

  • 一個EF層來讀/寫到我的數據庫
  • 其使用我的實體(讀/寫),並使得一些計算(I使用任務並行庫多線程服務在框架中)

如何在每個線程中同步我的對象上下文? 你知道一個好的模式來使它工作嗎?

+4

確切地說,你的問題是什麼? ObjectContext是一個工作單元。它不是線程安全的,也不需要。每個任務使用一個OC,你會沒事的。在線程之間共享一個,你就會遇到麻煩。 – 2010-07-15 18:09:12

+0

如果我每個任務使用一個OC,我想同步我的OC。例如,當我在thread1中執行SaveChanges()時,我想刷新我在thread2中的實體。 – 2010-07-15 18:30:44

+7

如果你想這樣做,你可能不會打擾線程,因爲你的任務實際上並不是獨立的。 – 2010-07-15 20:39:32

回答

0

我認爲克雷格可能是正確的關於您的申請需要有線程..但你可能會尋找ConcurrencyCheck在你的模型的使用,以確保你沒有「越權」更改

0

我不不知道你的應用程序有多少是真正的數字處理。如果速度是使用多線程的動機,那麼它可能會退後一步,收集有關下一個瓶子的位置的數據。

在很多情況下,我發現使用數據庫服務器的應用程序的限制因素是存儲I/O系統的速度。例如,硬盤驅動器的速度及其配置可能會產生巨大的影響。具有7,200 RPM的單個硬盤驅動器每秒可處理大約60個事務(取決於許多因素,球場數字)。

所以我的建議是首先措施並找出下一個瓶子的位置。你甚至不需要線程。這將使代碼更容易維護,並且質量很可能要高得多。

3

很好的建議是 - 只是不:-)英孚基本上設法生存一個線程 - 野獸的性質。

如果您絕對必須使用它,請創建最輕的DTO-s,儘快關閉OC,重新包裝數據,產生線程只是爲了進行計算而不做其他任何事情,等到它們完成時,然後創建另一個OC並將數據轉儲回數據庫,調和它等。

如果另一個「主」線程(通過TPL產生N個計算線程的線程)需要知道某個線程是否完成了火災事件,只需設置一個標誌在其他線程中,然後讓它的代碼檢查它的循環中的標誌,並通過創建新的OC來做出反應,然後在必要時協調數據。

如果你的情況更簡單,你可以適應這一點 - 關鍵是你只能設置一個標誌,讓另一個線程做好準備。這意味着它處於一個穩定的狀態,已經完成了一輪所做的任何事情,並且可以在沒有競爭條件的情況下做事。使用互換操作重置標誌(一個int),並保留一些定時數據,以確保您的線程在一段時間內不會再發生反應。T - 其他線路,他們可以花一生時間來查詢數據庫。

0

「我怎樣才能在每個線程中同步我的對象上下文?」 這將是艱難的。首先,SP或DB查詢可以具有並行執行計劃。所以,如果你在對象上下文中也有並行性,你必須手動確保你有足夠的隔離,但只要你不要鎖太長,導致死鎖。

所以我會說不需要這樣做。

但這可能不是你想要的答案。所以你可以解釋一下你想用這個多線程來實現什麼。它是更多的計算限制還是IO限制。如果它是IO綁定長時間運行的操作,那麼看看Jeff Richter的APM。

0

我認爲你的問題更多的是關於線程之間的同步,而EF在這裏是無關緊要的。如果我理解正確,當主線程執行一些操作時(例如「SaveChanges()」操作),您想要通知一個線程。這裏的線程就像客戶端 - 服務器應用程序,其中一個線程是服務器,其他線程是客戶端,並且您希望客戶端線程對服務器活動作出反應。

有人發現你可能不需要線程,但讓我們保持原樣。

只要你打算每個線程使用單獨的OC,就不用擔心死鎖。

我還假設你的客戶端線程是在某種循環中長時間運行的線程。如果您希望您的代碼在客戶端線程上執行,則不能使用C#事件。

class ClientThread { 
public bool SomethingHasChanged; 

    public MainLoop() 
    { 
    Loop { 
     if (SomethingHasChanged) 
     { 
     refresh(); 
     SomethingHasChanged = false; 
     } 

     // your business logic here 


    } // End Loop 
    } 
} 

現在的問題是你將如何在所有客戶端線程中設置標誌?您可以在主線程中保留對客戶端線程的引用,並循環遍歷它們並將所有標誌設置爲true。

0

當我使用EF時,我只有一個ObjectContext,我同步了所有訪問。

這並不理想。你的數據庫層實際上是單線程的。但是,它確實在多線程環境中保持線程安全。在我的情況下,大量的計算根本不在數據庫代碼中 - 這是一個遊戲服務器,所以遊戲邏輯當然是主要資源。所以,我沒有特別需要多線程數據庫層。

1

這就是我實現它的方式。

var processing= new ConcurrentQueue<int>(); 


//possible multi threaded enumeration only processed non-queued records 
Parallel.ForEach(dataEnumeration, dataItem=> 
{ 
    if(!processing.Contains(dataItem.Id)) 
    { 
     processing.Enqueue(dataItem.Id); 

      var myEntityResource = new EntityResource(); 

      myEntityResource.EntityRecords.Add(new EntityRecord 
             { 
             Field1="Value1", 
             Field2="Value2" 
             } 
           ); 

      SaveContext(myEntityResource); 

     var itemIdProcessed = 0; 
     processing.TryDequeue(out itemIdProcessed); 

    } 

} 

public void RefreshContext(DbContext context) 
    { 
     var modifiedEntries = context.ChangeTracker.Entries() 
      .Where(e => e.State == EntityState.Modified || e.State == EntityState.Deleted); 
     foreach (var modifiedEntry in modifiedEntries) 
     { 
      modifiedEntry.Reload(); 
     } 
    } 

public bool SaveContext(DbContext context,out Exception error, bool reloadContextFirst = true) 
    { 
     error = null; 
     var saved = false; 
     try 
     { 
      if (reloadContextFirst) 
       this.RefreshContext(context); 
      context.SaveChanges(); 
      saved = true; 
     } 
     catch (OptimisticConcurrencyException) 
     { 
      //retry saving on concurrency error 
      if (reloadContextFirst) 
       this.RefreshContext(context); 
      context.SaveChanges(); 
      saved = true; 
     } 
     catch (DbEntityValidationException dbValEx) 
     { 
      var outputLines = new StringBuilder(); 
      foreach (var eve in dbValEx.EntityValidationErrors) 
      { 
       outputLines.AppendFormat("{0}: Entity of type \"{1}\" in state \"{2}\" has the following validation errors:", 
        DateTime.Now, eve.Entry.Entity.GetType().Name, eve.Entry.State); 
       foreach (var ve in eve.ValidationErrors) 
       { 
        outputLines.AppendFormat("- Property: \"{0}\", Error: \"{1}\"", ve.PropertyName, ve.ErrorMessage); 
       } 
      } 

      throw new DbEntityValidationException(string.Format("Validation errors\r\n{0}", outputLines.ToString()), dbValEx); 
     } 
     catch (Exception ex) 
     { 
      error = new Exception("Error saving changes to the database.", ex); 
     } 
     return saved; 
    }