你有一些很好的建議在多線程程序中使用EF嗎?在多線程程序中使用EF的好建議?
我有2層:
- 一個EF層來讀/寫到我的數據庫
- 其使用我的實體(讀/寫),並使得一些計算(I使用任務並行庫多線程服務在框架中)
如何在每個線程中同步我的對象上下文? 你知道一個好的模式來使它工作嗎?
你有一些很好的建議在多線程程序中使用EF嗎?在多線程程序中使用EF的好建議?
我有2層:
如何在每個線程中同步我的對象上下文? 你知道一個好的模式來使它工作嗎?
我認爲克雷格可能是正確的關於您的申請需要有線程..但你可能會尋找ConcurrencyCheck在你的模型的使用,以確保你沒有「越權」更改
我不不知道你的應用程序有多少是真正的數字處理。如果速度是使用多線程的動機,那麼它可能會退後一步,收集有關下一個瓶子的位置的數據。
在很多情況下,我發現使用數據庫服務器的應用程序的限制因素是存儲I/O系統的速度。例如,硬盤驅動器的速度及其配置可能會產生巨大的影響。具有7,200 RPM的單個硬盤驅動器每秒可處理大約60個事務(取決於許多因素,球場數字)。
所以我的建議是首先措施並找出下一個瓶子的位置。你甚至不需要線程。這將使代碼更容易維護,並且質量很可能要高得多。
很好的建議是 - 只是不:-)英孚基本上設法生存一個線程 - 野獸的性質。
如果您絕對必須使用它,請創建最輕的DTO-s,儘快關閉OC,重新包裝數據,產生線程只是爲了進行計算而不做其他任何事情,等到它們完成時,然後創建另一個OC並將數據轉儲回數據庫,調和它等。
如果另一個「主」線程(通過TPL產生N個計算線程的線程)需要知道某個線程是否完成了火災事件,只需設置一個標誌在其他線程中,然後讓它的代碼檢查它的循環中的標誌,並通過創建新的OC來做出反應,然後在必要時協調數據。
如果你的情況更簡單,你可以適應這一點 - 關鍵是你只能設置一個標誌,讓另一個線程做好準備。這意味着它處於一個穩定的狀態,已經完成了一輪所做的任何事情,並且可以在沒有競爭條件的情況下做事。使用互換操作重置標誌(一個int),並保留一些定時數據,以確保您的線程在一段時間內不會再發生反應。T - 其他線路,他們可以花一生時間來查詢數據庫。
「我怎樣才能在每個線程中同步我的對象上下文?」 這將是艱難的。首先,SP或DB查詢可以具有並行執行計劃。所以,如果你在對象上下文中也有並行性,你必須手動確保你有足夠的隔離,但只要你不要鎖太長,導致死鎖。
所以我會說不需要這樣做。
但這可能不是你想要的答案。所以你可以解釋一下你想用這個多線程來實現什麼。它是更多的計算限制還是IO限制。如果它是IO綁定長時間運行的操作,那麼看看Jeff Richter的APM。
我認爲你的問題更多的是關於線程之間的同步,而EF在這裏是無關緊要的。如果我理解正確,當主線程執行一些操作時(例如「SaveChanges()」操作),您想要通知一個線程。這裏的線程就像客戶端 - 服務器應用程序,其中一個線程是服務器,其他線程是客戶端,並且您希望客戶端線程對服務器活動作出反應。
有人發現你可能不需要線程,但讓我們保持原樣。
只要你打算每個線程使用單獨的OC,就不用擔心死鎖。
我還假設你的客戶端線程是在某種循環中長時間運行的線程。如果您希望您的代碼在客戶端線程上執行,則不能使用C#事件。
class ClientThread {
public bool SomethingHasChanged;
public MainLoop()
{
Loop {
if (SomethingHasChanged)
{
refresh();
SomethingHasChanged = false;
}
// your business logic here
} // End Loop
}
}
現在的問題是你將如何在所有客戶端線程中設置標誌?您可以在主線程中保留對客戶端線程的引用,並循環遍歷它們並將所有標誌設置爲true。
當我使用EF時,我只有一個ObjectContext,我同步了所有訪問。
這並不理想。你的數據庫層實際上是單線程的。但是,它確實在多線程環境中保持線程安全。在我的情況下,大量的計算根本不在數據庫代碼中 - 這是一個遊戲服務器,所以遊戲邏輯當然是主要資源。所以,我沒有特別需要多線程數據庫層。
這就是我實現它的方式。
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;
}
確切地說,你的問題是什麼? ObjectContext是一個工作單元。它不是線程安全的,也不需要。每個任務使用一個OC,你會沒事的。在線程之間共享一個,你就會遇到麻煩。 – 2010-07-15 18:09:12
如果我每個任務使用一個OC,我想同步我的OC。例如,當我在thread1中執行SaveChanges()時,我想刷新我在thread2中的實體。 – 2010-07-15 18:30:44
如果你想這樣做,你可能不會打擾線程,因爲你的任務實際上並不是獨立的。 – 2010-07-15 20:39:32