VB.net多线程与许多数据库调用

时间:2015-10-20 17:24:23

标签: vb.net multithreading task-parallel-library sql-server-ce

我有一个vb.net应用程序,用户必须处理数百万条记录才能存储在数据库(sql CE)中。处理如下:

  1. 从数据库中检索一些已保存的数据以实例化对象。
  2. 执行一些计算。
  3. 将计算出的对象数据保存到数据库中的其他表中。
  4. 如果顺序完成,前两个项目占用大约30%的时间,最后一个项目占用大约70%的时间。

    我认为将大部分数据库写入更像是在单独的线程上进行批处理可能是有益的,希望通过(在理想世界中)减少执行第1项和第2项所需的30%的成本。尝试通过将处理过的对象存储在列表中来做到这一点,并且每当列表计数超过在单独线程上调用操作的特定数量时,我保存数据。每次保存数据时,我实际上都保存了主对象和几个相关子对象的数据,即

    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
    

    然而,这个实现比我之前的尝试慢得多,实际上似乎是同步操作。知道为什么这个方法实际上更慢?

    如果有其他关于如何加快整个过程的想法,我也会喜欢反馈。

2 个答案:

答案 0 :(得分:0)

使用TableDirect API来避免查询处理器的开销。对于插入和选择,您将看到巨大的速度增益。并且记住一个sql ce数据库是一个单独的文件,因此它可以并行地进行限制。 我最近有一些关于如何使用TableDirect apis

的deatils博客文章

答案 1 :(得分:-1)

Tasks.Task.Factory.StartNew不一定会创建新的后台线程。它创建了一个可以异步执行的任务。相反,您是否想要使用Parallel.Invoke

相关问题