2010-01-21 4 views
8

C#/.NET 사용 3.5.SqlDataAdapter.Fill - 비동기 접근

현재 SqlDataAdapter.Fill()을 사용하여 2 개의 DataTable을 차례로 채우고 있습니다.

각 데이터 테이블을 동시에 비동기 적으로 처리하여 병렬로 채우고 싶습니다. 그러나 Fill() 메서드의 비동기 버전은 없습니다. 즉 BeginFill()이 좋습니다!

  1. SqlCommand1.BeginExecuteReader // 1 쿼리, DataTable1에 대한
  2. SqlCommand2.BeginExecuteReader // 두번째 쿼리, DataTable2
  3. SqlCommand1.EndExecuteReader에 : 나는 시도했다

    한 가지 방법은 (의사)입니다

  4. SqlCommand2.EndExecuteReader
  5. DataTable1.Load (DataReader1)
  6. DataTable2.Load (DataReader2)

그러나 DataTable.Load()는 오랜 시간이 걸린다 :
가 그것은 5 다음 22초 소요 4.
공정 단계로 단계 1을 3 초 걸린다.
6 단계는 17 초 걸립니다.
그래서 5 단계와 6 단계에서 39 초를 합친 것입니다.

결과적으로 2 개의 SqlDataAdapter.Fills를 하나씩 수행하는 것 이상의 이점이 없습니다. 나는 순수한 결과가 전체 프로세스가 오직 가장 긴 쿼리 (또는 가능한 한 그것에 가깝게)만큼 오래 걸리기를 원한다.

DataTable을 작성하기위한 진정한 비동기식 접근 방법 인 뭔가를 제안하는 것이 좋습니다.

아니면 그냥 직접 관리하고 DataTable을 채우는 2 개의 개별 스레드를 굴립니까?

답변

5

각각 별도의 작업자 스레드가 있어야합니다. ThreadPool.QueueUserWorkItem을 사용할 수 있습니다.

List<AutoResetEvent> events = new List<AutoResetEvent>(); 

AutoResetEvent loadTable1 = new AutoResetEvent(false); 
events.Add(loadTable1); 
ThreadPool.QueueUserWorkItem(delegate 
{ 
    SqlCommand1.BeginExecuteReader; 
    SqlCommand1.EndExecuteReader; 
    DataTable1.Load(DataReader1); 
    loadTable1.Set(); 
}); 

AutoResetEvent loadTable2 = new AutoResetEvent(false); 
events.Add(loadTable2); 
ThreadPool.QueueUserWorkItem(delegate 
{ 
    SqlCommand2.BeginExecuteReader; 
    SqlCommand2.EndExecuteReader; 
    DataTable2.Load(DataReader2); 
    loadTable2.Set(); 
}); 

// wait until both tables have loaded. 
WaitHandle.WaitAll(events.ToArray()); 
+0

, 완료 했습니까? 계속 진행하기 전에 두 테이블을 모두 채워야합니다. – AdaTheDev

+0

도움이된다면 기다리는 개념을 제 대답에 추가했습니다. –

+0

@AdaTheDev, 작업이 완료된 후에 (개별 스레드 내에서) 트리거 할 AutoResetEvents를 사용합니다. @Neils는 이미 예제를 제공 했으므로 답변을 참조하십시오. – James

1

DataTable에는 많은 개체 (행, 값)가 있기 때문입니다. 어댑터의 실행과 데이터 테이블의 채우기를 모두 다른 스레드에서 수행해야하며 계속하기 전에 각 작업이 완료 될 때까지 기다리는 것과 동기화해야합니다. 나는 둘 때까지 기다릴 수있는 방법을

다음 코드는 메모장에서 작성되었으며 아마 컴파일되지 않습니다, 그러나 희망 당신은 아이디어를 얻을 ... 내가 하나 하나 개까지 대기중인 한 그래서 일단

// Setup state as a parameter object containing a table and adapter to use to populate that table here 

void DoWork() 
{ 
    List<AutoResetEvent> signals = GetNumberOfWaitHandles(2); 

    var params1 = new DataWorkerParameters 
     { 
      Command = GetCommand1(); 
      Table = new DataTable(); 
     } 

    var params2 = new DataWorkerParameters 
     { 
      Command = GetCommand2(); 
      Table = new DataTable(); 
     } 

    ThreadPool.QueueUserWorkItem(state => 
     { 
      var input = (DataWorkerParameters)state; 
      PopulateTable(input); 
      input.AutoResetEvent.Set(); // You can use AutoResetEvent.WaitAll() in the caller to wait for all threads to complete 
     }, 
     params1 
    ); 

    ThreadPool.QueueUserWorkItem(state => 
     { 
      var input = (DataWorkerParameters)state; 
      PopulateTable(input); 
      input.AutoResetEvent.Set(); // You can use AutoResetEvent.WaitAll() in the caller to wait for all threads to complete 
     }, 
     params2 
    ); 

    WaitHandle.WaitAll(signals.ToArray()); 
} 


void PopulateTable(DataWorkerParameters parameters) 
{ 
    input.Command.ExecuteReader(); 
    input.Table.Load(input.Command); 
} 
+0

고마워요 (둘 다). STA Thread에서 실행 중이므로 WaitHandle.WaitAll ("STA 스레드에서 여러 핸들에 대한 WaitAll이 지원되지 않음")을 사용할 수 없다는 것을 의미합니다. 대안이 있습니까? – AdaTheDev

+0

'Action' 콜백을 제공 할 수 있습니다.이 콜백은 충분한 시간 동안 호출되었을 때 다음 비트의 코드만을 수행합니까? 별로 좋지는 않지만 작동해야합니다. –

+0

제안 및 도움에 환호합니다. 필자는 내가 원하는 것을하기에 충분한 작업을했다. (서로의 주장과 병행하여 쿼리를 병렬로 실행하는 것이 실제로 도움이되지 않았다.) +1로 대답했습니다. – AdaTheDev