VB.NET 异步运行多个 SQL 命令

VB.NET Run Multiple SQL Commands Asynchronously

提问人:LordRofticus 提问时间:12/15/2022 最后编辑:marc_sLordRofticus 更新时间:12/15/2022 访问量:196

问:

我在 SQL Server 数据库中有很多存储过程,需要按顺序执行。

我将它们设置在一个表中,该表具有多个序列,每个序列最多包含 5 个存储过程 - 因此基本上是 5 个线程:

enter image description here

我想遍历每个序列并同时启动每个序列的存储过程。然后,当所有存储过程都完成时,可以启动下一个存储过程序列。

enter image description here

我将使用 .NET 中的 SQL 命令来启动每个存储过程,但我需要一种方法让它们同时启动,并让该过程等待所有命令完成,然后再继续下一个序列。

这样做的目的是为了节省处理数据的时间。如果每个存储过程运行 2 分钟,则一次运行 5 个存储过程将需要 2 分钟,而不是 10 分钟。

现在我被告知TPL将是一个很好的方法。这是我第一次使用多线程/ TPL。

下面是一个类,其中包含用于执行 SQL Server 存储过程的属性和函数:

Public Class clsSQLStoredProcedure

Private mConnection As SqlClient.SqlConnection
Private mCommand As SqlClient.SqlCommand
Private mSQLStoredProcedure As String
Private mName As String
Private mStatus As String = "Pending"
Private mFeedback As DataTable
Public Property Connection() As SqlClient.SqlConnection
    Set(ByVal o As SqlClient.SqlConnection)
        mConnection = o
    End Set
    Get
        Return mConnection
    End Get
End Property

Public Property SQLStoredProcedure() As String
    Set(ByVal o As String)
        mSQLStoredProcedure = o
    End Set
    Get
        Return mSQLStoredProcedure
    End Get
End Property

Public Property Name() As String
    Set(ByVal o As String)
        mName = o
    End Set
    Get
        Return mName
    End Get
End Property

Public ReadOnly Property Status() As String
    Get
        Return mStatus
    End Get
End Property

Public ReadOnly Property Feedback() As DataTable
    Get
        Return mFeedback
    End Get
End Property

Public Function ExecuteSQLStoredProcedure()

    On Error GoTo Errorhandler
    Debug.Print(mName & " Start @ " & Now().ToString)
    mStatus = "Running"
    If mConnection.State <> ConnectionState.Open Then mConnection.Open()
    mCommand = New SqlClient.SqlCommand(mSQLStoredProcedure, mConnection)
    mCommand.CommandTimeout = 0
    mCommand.ExecuteNonQueryAsync()
    mStatus = "Completed"
    Debug.Print(mName & " Completed @ " & Now().ToString)
    Exit Function

Errorhandler:
    mStatus = "Failed"
    Debug.Print(mName & " Failed @ " & Now().ToString & " - " & Err.Description)

End Function

End Class

这是我用来执行任务 (TPL) 以同时启动 3 个存储过程的代码:

 Imports System.Threading
 Imports System.Threading.Tasks

 Module modThreading

Public Sub TestThreading()

    Dim oSP1 As New clsSQLStoredProcedure
    oSP1.SQLStoredProcedure = "EXEC CURRO_DW.conform.usp_ETLFactCollectorActivity -99"
    oSP1.Connection = gGDM.Database.Connection
    oSP1.Name = "usp_ETLFactCollectorActivity"
    'oSP1.ExecuteSQLStoredProcedure()
    Task.Run(action:=oSP1.ExecuteSQLStoredProcedure())

    Dim oSP2 As New clsSQLStoredProcedure
    oSP2.SQLStoredProcedure = "EXEC CURRO_DW.conform.usp_ETLDimPerson -99"
    oSP2.Connection = gGDM.Database.Connection
    oSP2.Name = "usp_ETLDimPerson"
    'oSP2.ExecuteSQLStoredProcedure()
    Task.Run(action:=oSP2.ExecuteSQLStoredProcedure())

    Dim oSP3 As New clsSQLStoredProcedure
    oSP3.SQLStoredProcedure = "SELECT 1"
    oSP3.Connection = gGDM.Database.Connection
    oSP3.Name = "TEST"
    'oSP3.ExecuteSQLStoredProcedure()
    Task.Run(action:=oSP3.ExecuteSQLStoredProcedure())


    MsgBox("Done")

End Sub

End Module

然而,它们似乎仍然一个接一个地运行,尽管有些应该立即运行,而另一些则需要大约 1 分钟。

sql-server vb.net 多线程 任务并行库

评论

0赞 AlwaysLearning 12/15/2022
当您 3 天前的问题被关闭时,有人建议“您应该从研究任务并行库 (TPL) 开始”,并不一定是一个好的方法。似乎您需要继续阅读 TPL 文档,以便您可以将 Task.WaitAll 等方法合并到您的解决方案中。
0赞 LordRofticus 12/15/2022
因此,我为什么要在这里发帖,也许有人可以对更好的方法来使该解决方案发挥作用有所了解。
0赞 LordRofticus 12/15/2022
我也尝试了 WaitAll,但 StoredProcedure 仍然没有异步运行

答:

1赞 djv 12/15/2022 #1

不要在应用程序的整个生命周期内保持连接打开状态。应分别尽可能晚和尽早打开和关闭数据库连接。数据库连接在单个线程上运行,因此第一个任务虽然是异步运行的,但会阻塞第二个任务,依此类推。

因此,为每个存储过程创建一个新连接,也许在最后创建一个连接。您可以将 Tasks 放在集合中来执行此操作。WaitAll

Dim gGDM1 As New DatabaseContext()
Dim gGDM2 As New DatabaseContext()
Dim gGDM3 As New DatabaseContext()

Try
    Dim oSP1 As New clsSQLStoredProcedure
    oSP1.SQLStoredProcedure = "EXEC CURRO_DW.conform.usp_ETLFactCollectorActivity -99"
    oSP1.Connection = gGDM1.Database.Connection
    oSP1.Name = "usp_ETLFactCollectorActivity"

    Dim oSP2 As New clsSQLStoredProcedure
    oSP2.SQLStoredProcedure = "EXEC CURRO_DW.conform.usp_ETLDimPerson -99"
    oSP2.Connection = gGDM2.Database.Connection
    oSP2.Name = "usp_ETLDimPerson"

    Dim oSP3 As New clsSQLStoredProcedure
    oSP3.SQLStoredProcedure = "SELECT 1"
    oSP3.Connection = gGDM3.Database.Connection
    oSP3.Name = "TEST"

    Dim tasks As New List(Of Task)()
    tasks.Add(Task.Run(oSP1.ExecuteSQLStoredProcedure))
    tasks.Add(Task.Run(oSP2.ExecuteSQLStoredProcedure))
    tasks.Add(Task.Run(oSP3.ExecuteSQLStoredProcedure))
    Task.WaitAll(tasks.ToArray())
Finally
    gGDM1.Dispose()
    gGDM2.Dispose()
    gGDM3.Dispose()
    MsgBox("Done")
End Try

您需要修改行以创建新上下文,但是您已经全局执行此操作,而是针对每个调用。完成后,它们会立即被处理掉。Dim gGDM As New DatabaseContext()