1

我有一個vb.net應用程序,其中用戶必須處理數百萬條記錄以存儲在數據庫(sql CE)中。處理過程如下:VB.net多線程與許多數據庫調用

  1. 從數據庫中檢索一些保存的數據以實例化一個對象。
  2. 執行一些計算。
  3. 將計算的對象數據保存到數據庫中的其他表中。

如果按順序進行,前兩個項目佔用大約30%的時間,最後一個項目佔用大約70%的時間。

我認爲將數據庫的大部分寫入更像一個單獨的線程上的批處理過程可能是有益的,希望通過(在理想的世界中)減少30%的成本來完成項目1和2。我試圖通過將處理過的對象存儲在一個列表中,並且每當列表計數超過一個在獨立線程上調用某個操作的數字時,我就會保存這些數據。每個數據保存時間,我居然保存數據的主要目的和幾個相關的子對象,即

cmd.CommandText = "INSERT INTO [MainObjectTable] (Data1, Data2, Data3) VALUES (@Data1, @Data2, @Data3)" 
cmd.ExecuteNonQuery() 

cmd2.CommandText = "SELECT MAX(idx) FROM [MainObjectTable]" 
MainObjectIdx = CInt(cmd2.ExecuteScalar()) 

'Loop over child objects and save each one to database' 
cmd3.CommandText = "INSERT INTO [ChildObject] (MainObjectIdx, Data4, Data5, Data6) VALUES (MainObjectIdx, @Data4, @Data5, @Data6)" 

for i = 0 to ChildList.Count -1 
    [Code to Update parameters for the command object] 
    cmd3.ExecuteNonQuery() 
next 

我鎖定數據庫,以防止多個記錄,從設法保存一次。我必須這樣做(我認爲),因爲我使用主對象的記錄鍵來進一步處理子對象的數據,如上所示。

的代碼基本上看起來是這樣的:

Private sub CalcData() 
    Dim objectList as new List(of ParentObject) 
    For k = 0 to Calculations 
     'Load data, instantiate object' 
     Dim o as new ParentObject(IDs(k)) '<-- This pulls records from the sql ce database and loads the object' 
     o.calcData 'calculate the new data based on loaded data' 
     objectList.add(o) 'store in a list' 
     If objectList.Count > 1000 then 
      Batchsave(objectList) 
      objectList.clear() 
     End If 
    Next 
End Sub 

Private Sub BatchSave(objList As List(of ParentObject))           
    mTaskList.Add(Tasks.Task.Factory.StartNew(
      Sub() 
       DBLock.EnterWriteLock() 
       Try 
        for j = 0 to objectList.count-1 
        [Code to update command object parameters, and save the object (and children) as discussed above where I show the sql statements] 
        next 
       Finally 
        DBLock.ExitWriteLock() 
       End Try 
      End Sub))    
End Sub 

我想這個方案將性能最大化,從而將數據保存到可以在後臺線程完成。我像一個批處理一次構建了保存(一次處理1000條記錄),因爲我讀過更新許多記錄時參數化sql更有效。但時間縮短並不令人印象深刻。

我也嘗試創建一個新的「保存」類,我通過數據保存,因爲它變得可用。每當父對象傳遞給它時,「Save」類將處理創建一個新的tasks.task,所以我認爲這或多或少會創建一個連續的對象流來保存在其他線程上,而不是依靠保存每1000個對象。裏面的「保存」類我有以下幾點:

Public Class SaveData 

Public Sub SaveBDLItem(ByVal o As ParentObject) 

    Tasks.Task.Factory.StartNew(
      Sub() 
       Dim Object 
       mParentLock.EnterWriteLock() 
       Try 
        mcmd1.Parameters.Clear() 

        [code to add parameters to command object] 

        mcmd1.ExecuteNonQuery() 
        'get new key ' 
        objectIDx= CInt(mcmd2.ExecuteScalar()) 
       Finally 
        mBDLLock.ExitWriteLock() 
       End Try 

       'Now update children' 
       mChildLock.EnterWriteLock() 
       Try 
        For j = 0 To ParentObject.Children.Count - 1 
         mcmd3.Parameters.Clear() 

         [code to add parameters to command object] 

         mcmd3.ExecuteNonQuery() 

        Next 
       Finally 
        mChildLock.ExitWriteLock() 
       End Try 

      End Sub)) 

End Sub 
. 
. 
. 
End Class 

然而,這種執行是比我以前嘗試慢得多,事實上,似乎同步操作。任何想法爲什麼這種方法實際上更慢?

我還喜歡反饋,如果有其他想法如何加快整個過程。

+0

多線程不會*加速緩慢的數據庫操作,它通常會讓事情變得更糟。要麼批處理所有語句並將它們作爲一個執行,要麼使用TableDirect –

+0

爲什麼不呢?如果30%的時間花費在計算上,而70%花在寫入數據庫上,那麼在我看來,將數據庫寫入單獨的線程將節省多達30%。在向後臺寫入數據庫chug時可以運行更快的計算,從而有效地消除了30%的計算成本。 – DrCocoa

+0

@PanagiotisKanavos - 哦,我明白了。我只是重讀你的評論。爲了澄清,我沒有使用多線程來加速數據庫操作。我正在使用多線程在計算和數據庫操作之間進行多任務。對於這種情況,多線程工作正常,因爲它使我能夠並行而不是串行地進行計算和寫入數據庫。 – DrCocoa

回答

-1

Tasks.Task.Factory.StartNew不一定會創建新的後臺線程。它創建一個任務可以異步執行。相反,您是否想使用Parallel.Invoke

+0

PLINQ使用任務本身。實際上,PLINQ也會使用當前線程。它是Parallel.Invoke,如果只提供一個委託,*不會在後臺運行 –

0

使用TableDirect API來避免查詢處理器的開銷。對於插入和選擇,您將看到巨大的速度增益。並且記住一個sql ce數據庫是一個單獨的文件,所以它可以並行執行的操作受到限制。 我有一些關於如何使用TableDirect最近的博客帖子,關於如何使用TableDirect apis