SqlDataAdapter.Fill - Асинхронный подход - PullRequest
8 голосов
/ 21 января 2010

Использование C # / .NET 3.5.

В настоящее время я заполняю 2 DataTables один за другим, используя SqlDataAdapter.Fill ().

Я хочу заполнить обе эти таблицы данных параллельно, выполняя каждую из них асинхронно. Однако асинхронной версии метода Fill () не существует, т. Е. Было бы здорово BeginFill ()!

Один из подходов, которые я пробовал, (псевдо):

  1. SqlCommand1.BeginExecuteReader // 1-й запрос, для DataTable1
  2. SqlCommand2.BeginExecuteReader // 2-й запрос, для DataTable2
  3. SqlCommand1.EndExecuteReader
  4. SqlCommand2.EndExecuteReader
  5. DataTable1.Load (DataReader1)
  6. DataTable2.Load (DataReader2)

Однако DataTable.Load () занимает много времени:
Шаг 1 - шаг 4 занимает 3 секунды.
Шаг 5 затем занимает 22 секунды.
Шаг 6 занимает 17 секунд.
Итак, в сумме 39 секунд для шагов 5 и 6.

Конечный результат заключается в том, что это не дает мне никакой выгоды по сравнению с выполнением двух SqlDataAdapter.Fills, один за другим. Я хочу, чтобы итоговым результатом было то, что весь процесс занимает столько времени, сколько длится самый длинный запрос (или как можно ближе к нему).

В поисках рекомендуемых путей продвижения к чему-то, что является действительно асинхронным подходом к заполнению DataTable.

Или я сам управляю им и запускаю 2 отдельных потока, каждый из которых заполняет DataTable?

Ответы [ 2 ]

5 голосов
/ 21 января 2010

Я бы предложил иметь отдельный рабочий поток для каждого. Вы можете использовать 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());
1 голос
/ 21 января 2010

Это потому, что 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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...