2013-06-12 123 views
0

我正在寫一個WinForms應用程序。我從數據庫中提取數據,對該數據集執行一些操作,然後計劃將其保存回數據庫。我正在使用LINQ to SQL來執行查詢到數據庫,因爲我只關注數據庫中的一個表,所以我不想爲此實現一個完整的ORM。多線程與LINQ to SQL

我從數據庫中提取數據集。但是,數據集相當大。所以目前我試圖做的是將數據集分成4個相對大小的列表(List<object>)。

然後,我有一個獨立的後臺工作人員通過每個名單中運行,執行操作,並且同時也彙報了進展情況。一旦所有4位後臺工作人員完成處理,我計劃將這些部分合併成一個大名單。

但我不斷收到一個錯誤,而背景工人正在處理他們的唯一列表。即使對象已轉換爲List對象,對象是否仍然保持與LINQ to SQL的DataContext綁定?任何想法如何解決這一問題?我對多線程的經驗很少,所以如果我對這個完全錯誤的話,請告訴我。

謝謝你們。如果您需要任何代碼片段或任何其他信息,請詢問。

編輯:哎呀。我完全忘了給出錯誤信息。在DataContext designer.cs中,它在SendPropertyChanging函數上給出了錯誤An item with the same key has already been added.

private void Setup(){ 
    List<MyObject> quarter1 = _listFromDB.Take(5000).ToList(); 
    bgw1.RunWorkerAsync(); 
} 

private void bgw1_DoWork(object sender, DoWorkEventArgs e){ 
    e.Result = functionToExecute(bgw1, quarter1); 
} 

private List<MyObject> functionToExecute(BackgroundWorker caller, List<MyObject> myList) 
    { 
     int progress = 0; 
     foreach (MyObject obj in myList) 
     { 
      string newString1 = createString(); 
      obj.strText = newString; 
      //report progress here 
      caller.ReportProgress(progress++); 
     } 
     return myList; 
    } 

同樣的功能由四個工人打來電話,在此基礎上的工人被調用的函數,給出了myList中不同的列表。

+0

的對象引用被添加到列表,所以他們會保持聯繫。嘗試映射對象。 – Romoku

+2

您能否將您收到的錯誤添加爲您的問題的編輯? –

+0

該列表正在存儲對由DBContext正在創建的任何對象的引用,因此僅僅轉換爲列表本身不會執行任何操作.....不知道錯誤很難提供幫助:) –

回答

0

首先,我不明白爲什麼你需要多個工作線程。 (這些列表在單獨的數據庫/表/服務器?你真的想顯示4個進度條,如果你有4個列表,或者你在某種程度上將這些進度報告合併成一個奇怪的進度條:D

另外,試圖加快處理數據庫的更新,但是你不會發送LINQ到SQL任何SAVES,所以你不是真正的批處理事務,你只需要在一個大事務中保存所有的東西,這真的是你我們的目標是,進度條會停止在100%,然後在SQL端花費大量時間。

只需創建一個後臺線程並同步處理所有內容,但每隔幾行就會批處理一次保存事務我建議像每1000行一樣,但你應該試試這個),它會很快,甚至有數百萬行,

如果你真的需要這個多線程解決方案: 「另一個同樣的密鑰已被添加」的錯誤表明你正在添加相同的項目多個「mylists 「,或者將同一個項目添加到同一個列表中兩次,否則會出現什麼錯誤?

+0

我還沒有保存任何東西。我在開始修改數據庫之前測試該函數是否按預期工作。我打算按照你的建議批量保存,但我還沒有到那一步。我設置了進度條以匹配記錄總量,每次處理記錄時,工作人員都會觸發progressChanged事件。在進度更改事件中,我正在進度欄中執行一個步驟。問題不在於我將同一項目添加到多個列表中,因爲我將它們添加到單個列表中時,我將它們從完整列表中刪除。 –

+0

我決定採用簡單的解決方案,只是做一個單一的後臺工作。我仍然有興趣知道如何做到這一點。 –

+0

我給你一個投票給你回答,因爲答案沒有解決手頭的問題 - 拋出錯誤是什麼?你的答案只是談論改變應用程序的邏輯到完全不同的東西。首先 - 總是解決問題本身。這樣,OP和其他人將會知道代碼有什麼問題,他們會得到更好的理解。然後,只有當你指出其他問題 - 如低效率的邏輯或更好的做事方式。順便說一下,我的洞衷心地同意你的答案中的點... – Marko

0

使用Parallel LINQ (PLINQ),你可以採取多個CPU內核的好處處理您的資料。但是,如果您的應用程序要在單核CPU上運行,那麼將數據拆分爲peaces不會帶來性能優勢,反而會產生一些上下文更改開銷。

希望它有助於

+0

順便說一句,對於你正在得到的錯誤,我猜測你的dbml文件有問題,因此我建議你刪除並重新創建它。 – Farzan

+0

我沒有在單核CPU上運行,但我不認爲PLINQ是我正在尋找的。這個問題不是來自我的LINQ語句從數據庫中獲取數據。這工作得很好,而且速度很快。當我的工作人員試圖修改從數據庫中提取的記錄時,就會出現問題。 –

+1

PLINQ只適用於LINQ to Objects。它不適用於LINQ to SQL。 –

1

因爲一個真正的答案尚未公佈,我給它一個鏡頭。 既然你還沒有表現出任何的LINQ到SQL代碼(沒有的DataContext的使用) - 我會採取一個受過教育的猜測,在DataContext是在線程之間共享,例如:

using (MyDataContext context = new MyDataContext()) 
{ 
    // this is just some random query, that has not been listed - ToList() 
    // thus query execution is defered. listFromDB = IQueryable<> 
    var listFromDB = context.SomeTable.Where(st => st.Something == true); 

    System.Threading.Tasks.Task.Factory.StartNew(() => 
    { 
     var list1 = listFromDB.Take(5000).ToList(); // runs the SQL query 
     // call some function on list1 
    }); 

    System.Threading.Tasks.Task.Factory.StartNew(() => 
    { 
     var list2 = listFromDB.Take(5000).ToList(); // runs the SQL query 
     // call some function on list2 
    }); 
} 

現在錯誤你得到 - An item with the same key has already been added. - 是因爲DataContext對象不是線程安全的!很多東西發生在後臺--DataContext必須從SQL加載對象,跟蹤它們的狀態等。這個後臺工作是拋出錯誤的原因(因爲每個線程正在運行查詢,DataContext被訪問)。

至少這是我自己的親身經歷。遇到同一個錯誤時,在多個線程之間共享DataContext。你只有在這種情況下兩種選擇:

1)啓動線程之前,請查詢.ToList(),使得listFromDB不是IQueryable<>,但實際List<>。這意味着查詢已經運行並且線程在實際列表上運行,而不是在DataContext上運行。

2)將DataContext定義移動到每個線程中。由於不再共享DataContext,因此不會出現更多錯誤。

第三個選擇是將場景重新編寫成別的東西,像你這樣(例如,讓一切順序在單個後臺線程)...