2014-04-24 9 views
0

첫 번째 타이머는 여기에 나와 있으므로 쉽게 이동하십시오. 이론적으로 두 개의 OleDBDataAdapter.Fill 호출을 별도의 스레드에서 동시에 실행할 수 있습니까? 아니면 근본적으로 결함이 있습니까?동시 OleDbDataAdapter.Fill 별도의 스레드에서 호출?

2 개의 버튼과 2 개의 데이터 그람저가있는 양식을 고려하십시오. 각 단추를 클릭하면 채워진 데이터 테이블을 반환하는 메서드를 호출하고이를 datagridview 중 하나에 할당하는 Async \ Await \ Task.Run 패턴을 사용하여 작업자 스레드가 시작됩니다. 첫 번째 스레드의 .Fill은 완료하는 데 30 초가 걸립니다. 두 번째 스레드의 .Fill은 완료하는 데 1 초가 걸립니다. 개별적으로 실행되면 두 단추가 모두 예상대로 작동합니다.

그러나 처음 작업자 스레드 (채우기 30 초)를 실행 한 다음 두 번째 스레드 (1 초 채우기)를 시작하면 첫 번째 .Fill 호출이 완료 될 때까지 두 번째 DataGridView가 채워지지 않습니다. 두 번째 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를 사용해 보았습니다.이 SqlDataAdapter는 SQL Server와 통신하고 있음을 나타냅니다. 이는 가능해야합니다. 아마도 여기에서 첫 번째 쿼리는 두 번째 쿼리에 필요한 데이터 중 일부를 잠그는 것입니다. transaction isolation level을 변경하거나 with (nolock) 힌트를 신중히 사용하여 이전 버전을 사용할 수 있습니다 (이전 옵션이 강력하게 권장 됨).

명심해야 할 점은 각 쿼리에 대해 별도의 연결을 사용하거나 특별히 여러 개의 활성 결과 집합 기능을 사용하는 경우에만 작동한다는 것입니다. 여기서는 별도의 연결 개체를 사용하고있는 것처럼 보입니다. 그래야 정상적으로 작동하지만 여전히 가치가 있다고 생각합니다.

마지막으로 사용자의 FillDataTable() 메소드에 대해 의견을 말합니다. 이 방법을 사용하려면 완료된 SQL 문자열을 제공해야합니다. 실제로 SQL 주입 공격에 취약 할 수있는 코드를 작성해야합니다. 실제로 표시된대로 메서드를 계속 사용하면 앱이 해킹당하는 것을 보장합니다. 나중에는 앱을 해킹하는 것이 더 빠를 것입니다. 매개 변수가있는 쿼리를 사용하도록이 메서드를 수정해야합니다.

+0

실제로 Access 데이터베이스를 사용하고 있지만 정확히 동일한 동작으로 SQL Server에 대한 테스트를 시뮬레이션했습니다. 두 경우 모두 관련없는 테이블을 완전히 질의하여 자신을 차단하지 못하게했습니다. 나는 SQL 인젝션 경고에 감사한다. 이것은 단지 빠르고 불결한 PoC winform 코드 일 뿐이다. 다른 생각? 연결 문자열 등의 특성을 고려하여 여기에 실행 준비 코드가 없다는 것을 알았지 만 문제를 재현 할 수 있는지 여부는 매우 궁금합니다. – rSquared

+0

실제로 각 스레드에서 서로 관련이없는 두 개의 Access 데이터베이스를 완전히 쿼리 할 경우 문제가 발생합니다. 즉, DataAdapter 객체의 본래의 것이 스레드간에 공유되는 것을 의미합니까? – rSquared

+0

@rSquared 나는 당신이 이것을하기 위해'async'를 사용하고 있다는 것을 깨달았습니다. 'async'가 다중 쓰레드를 사용하지 않는 ** 것을 이해하는 것이 중요합니다.그것은 같은 thread_에서 순서가 어긋나도록 허용합니다. 그것은 병렬성의 _appearance_을 제공하고, 매우 자주 (** 많은 **) CPU 또는 단일 스레드의보다 효율적인 사용을하지만, 실제로 병렬 아니에요. 데이터베이스 호출과 같이 차단하는 항목이 있으면 계속해서 하나의 호출 완료를보고 다른 호출은 계속 볼 것입니다. –