2014-04-24 53 views
0

第一次在這裏,所以對我來說很容易。理論上可以同時在單獨的線程上執行兩個OleDBDataAdapter.Fill調用嗎?或者是從根本上有缺陷?在單獨的線程上同時調用OleDbDataAdapter.Fill?

考慮一個包含2個按鈕和2個datagridviews的表單。每個按鈕單擊使用Async \ Await \ Task.Run模式啓動工作線程,該模式調用一個方法來返回填充的數據表並將其分配給其中一個數據網格視圖。第一個線程中的.Fill需要30秒才能完成。第二個線程中的.Fill需要1秒鐘才能完成。當單獨啓動時,兩個按鈕都按預期工作。但是,如果我啓動第一個工作線程(30秒填充),然後啓動第二個線程(1秒填充),第二個DataGridView不會填充,直到第一個.Fill調用完成。我期望第二個datagridview在1秒內填充,第一個datagridview在~30秒後填充。

我在示例代碼中使用OleDBDataAdapter和SqlDataAdapter重複了這個問題。如果我用一個簡單的Thread.Sleep(30000)替換長時間運行的查詢,則第二個datagridview會立即填充。這讓我相信,這不是我的設計模式的問題,而是特定於同時發出.Fill調用的問題。

Private Async Sub UltraButton1_Click(sender As Object, e As EventArgs) Handles UltraButton1.Click 

    Dim Args As New GetDataArguments 
    Args.ConnectionString = "some connection string" 
    Args.Query = "SELECT LongRunningQuery from Table" 

    Dim DT As DataTable = Await Task.Run(Function() FillDataTable(Args)) 
    If DataGridView1.DataSource Is Nothing Then 
     DataGridView1.DataSource = DT 
    Else 
     CType(DataGridView1.DataSource, DataTable).Merge(DT) 
    End If 

End Sub 

Function FillDataTable(Args As GetDataArguments) As DataTable 

    Dim DS As New DataTable 

    Using Connection As New OleDbConnection(Args.ConnectionString) 
     Using DBCommand As New OleDbCommand(Args.Query, Connection) 
      Using DataAdapter As New OleDbDataAdapter(DBCommand) 
       DataAdapter.Fill(DS) 
      End Using 
     End Using 
    End Using 

    Return DS 

End Function 

Private Async Sub UltraButton2_Click(sender As Object, e As EventArgs) Handles UltraButton2.Click 

    Dim DS As DataTable = Await Task.Run(Function() LoadSecondDGV("1234")) 
    DataGridView2.DataSource = DS 

End Sub 

Function LoadSecondDGV(pnum As String) As DataTable 

    Dim DX As New DataTable 

    Using xConn As New OleDbConnection("some connection string") 
     Using DataAdapter As New OleDbDataAdapter("Select name from products where PNUM = """ & pnum & """", xConn) 
      DataAdapter.Fill(DX) 
     End Using 
    End Using 

    Return DX 

End Function 

回答

0

這取決於數據源是什麼。一些數據源(如Excel)一次只允許一個連接。其他數據源(如Access)將允許多個連接,但實際上是以串行方式實現結果,這樣您就不會獲得任何收益。其他數據源(如Sql Server)將允許您尋找真正的並行活動。

在這種情況下,你提到你也嘗試了一個SqlDataAdapter,它告訴我你正在與Sql Server交談,這應該是可能的。這裏可能發生的是你的第一個查詢是鎖定你需要的第二個查詢的一些數據。您可以通過更改transaction isolation level或通過謹慎使用with (nolock)提示(以前的選項爲強烈首選)來解決此問題。

要記住的另一件事是,只有當您爲每個查詢使用單獨的連接,或者如果您已經特別啓用了「多活動結果集」功能時,這才能起作用。看起來你在這裏使用單獨的連接對象,所以你應該沒問題,但它仍然是我認爲值得提出的東西。

最後,我需要對您的FillDataTable()方法發表評論。這種方法要求您提供一個完整的Sql字符串,這實際上會迫使您編寫代碼,這些代碼將極易受到sql注入攻擊的攻擊。繼續使用如實際所示的方法可以確保您的應用程序會被黑客入侵,可能會更快,而不是晚些時候。您需要修改此方法,以鼓勵您使用參數化查詢。

+0

我實際上使用的是Access數據庫,但是我確實使用完全相同的行爲模擬了針對SQL Server的測試。我在兩種情況下都查詢完全無關的表格,以確保我不會阻止自己。我很欣賞sql注入警告 - 這只是一些快速和骯髒的PoC winforms代碼。任何其他想法?我意識到,鑑於連接字符串等的性質,我沒有執行就緒代碼,但我很想知道是否有人可以重現此問題。 – rSquared

+0

實際上,如果我在每個線程中查詢兩個完全分離的不相關的Access數據庫,問題依然存在。這意味着DataAdapter對象的某些內在特性正在跨線程共享,不是嗎? – rSquared

+0

@rSquared我剛剛意識到你正在使用'async'來做到這一點。理解'async'確實不**使用多線程很重要。它允許事物在同一個線程中按順序運行。它給出了並行的_appearance_,並且它經常使你的cpu或單線程更有效地使用(**很多),但它並不是實際上並行的。如果你有阻塞的東西,比如數據庫調用,你仍然會看到一個調用完成,然後是另一個。 –

相關問題