SqlDataAdapter.Fill - 异步方法
Posted
技术标签:
【中文标题】SqlDataAdapter.Fill - 异步方法【英文标题】:SqlDataAdapter.Fill - Asynchronous approach 【发布时间】:2010-01-21 11:50:06 【问题描述】:使用 C#/.NET 3.5。
目前我正在使用 SqlDataAdapter.Fill() 一个接一个地填充 2 个数据表。
我想并行填充这两个数据表,同时通过异步执行每个数据表。但是,Fill() 方法没有异步版本 - 即 BeginFill() 会很棒!
我尝试过的一种方法是(伪):
-
SqlCommand1.BeginExecuteReader // 第一个查询,用于 DataTable1
SqlCommand2.BeginExecuteReader // 第二次查询,用于 DataTable2
SqlCommand1.EndExecuteReader
SqlCommand2.EndExecuteReader
DataTable1.Load(DataReader1)
DataTable2.Load(DataReader2)
但是,DataTable.Load() 需要很长时间: 完成第 1 步到第 4 步需要 3 秒。 然后步骤 5 需要 22 秒。 第 6 步需要 17 秒。 因此,第 5 步和第 6 步总共需要 39 秒。
最终结果是,与一个接一个地执行 2 个 SqlDataAdapter.Fills 相比,这对我没有任何好处。我希望最终结果是整个过程只需要最长的查询(或尽可能接近)。
寻找推荐的方法来最终获得真正异步的方法来填充 DataTable。
还是我自己管理它并滚动 2 个单独的线程,每个线程填充一个 DataTable?
【问题讨论】:
【参考方案1】:我建议为每个单独的工作线程。你可以使用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());
【讨论】:
所以,一旦我把每一个都排好队,我怎么能等到两个都完成呢?我需要先填充两个表,然后才能继续处理它们 如果有帮助,我已将等待概念添加到我的答案中。 @AdaTheDev,您将使用在工作完成后触发的 AutoResetEvents(在每个单独的线程中)。请参阅@Neils 的答案,因为他已经提供了一个示例。 我已更新以向您展示(如果使用我的示例)您将如何实现这一点。 谢谢(两者)。我在 STA 线程上运行,这意味着我不能使用 WaitHandle.WaitAll(“不支持 STA 线程上的多个句柄的 WaitAll”)。有其他选择吗?【参考方案2】:这是因为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);
【讨论】:
谢谢(两者)。我在 STA 线程上运行,这意味着我不能使用 WaitHandle.WaitAll(“不支持 STA 线程上的多个句柄的 WaitAll”)。有其他选择吗? 你可以提供一个Action
回调,它只在被调用足够多次时才执行下一段代码?不是很好,但应该可以。
为建议和帮助干杯。我有足够的工作来做我想做的事(并行运行查询并没有真正的好处,因为它们相互竞争)。为答案+1,因为它确实有帮助。以上是关于SqlDataAdapter.Fill - 异步方法的主要内容,如果未能解决你的问题,请参考以下文章
SqlDataAdapter.Fill(DataGridView.DataSource) 复制所有行