背景:
我有一个服务,它会分批查询数据库中的历史结果。 批次基于开始时间和结束时间。所有批次之间的数据是互斥的,因此对于给定的批次集合,它们可以按任何顺序运行。有些批次可能比其他批次花费更多时间。如果任何批次失败,则中止/停止整个过程并记录错误。将数据返回给客户端时,需要组合所有批次的数据。
问题:
尽快将数据返回给客户端。
初步解决方案:
最初我是按顺序同步执行批次的。这并没有利用数据相互排斥的事实。初次使用后,发现这种加载方法耗时太长。经过一些调试,我发现缓慢的主要原因是sql查询的执行时间。因此,下一个解决方案是尝试使用BeginExecuteReader()
和EndExecuteReader()
异步执行每个批处理。在异步调用所有批处理之后,服务将在while循环中等待并每300ms轮询一次以检查是否已完成任何查询。如果是,则会被阅读。
int batchCount = 0, resultCount = 0;
List<AsyncDataResult> asyncCalls = new List<AsyncDataResult>();
while (true)
{
if (asyncCalls.Count == 0)
{
break;
}
if ((asyncCalls[resultCount]).AsyncResult.IsCompleted)
{
ReadAsyncResultsFromDb(asyncCalls[resultCount]);
asyncCalls.RemoveAt(resultCount);
}
resultCount++;
if (resultCount >= asyncCalls.Count)
{
resultCount = 0;
Thread.Sleep(300);
}
}
上述方法减少了大型数据集的加载时间,但是对于非常小的数据集(但是在许多批次中),轮询实际上增加了加载延迟。
问题:
更新:
对此抱歉,但我忘了在调用异步调用的同一方法中添加我需要返回的部分。这样做的原因是我需要填充的数据集作为参数传递给此方法。使用begin reader中的IAsyncCallback
将需要我更改整个类。我希望我不必这样做。
答案 0 :(得分:2)
为何积极投票?您生成的每个异步操作都会返回一个具有WaitHandle
的IAsyncResult。使用WaitAny()
并让系统通知您:
/// <summary>
/// Do something useful with a completed query
/// </summary>
/// <param name="result"></param>
/// <returns>
/// true if the query failed;
/// false if the query was successful
/// </returns>
private static bool DoSomethingUseful( IAsyncResult result )
{
throw new NotImplementedException() ;
}
static void Main( string[] args )
{
List<IAsyncResult> batch = SpawnBatch() ;
bool errorOccurred = ProcessCompletedBatches( batch , DoSomethingUseful) ;
if ( errorOccurred )
{
CancelPending( batch ) ;
}
return ;
}
public static bool ProcessCompletedBatches( List<IAsyncResult> pending , Func<IAsyncResult,bool> resultHandler )
{
bool errorOccurred = false ;
while ( ! errorOccurred && pending.Count > 0 )
{
WaitHandle[] inFlight = pending.Select( x => x.AsyncWaitHandle ).ToArray() ;
int offset = WaitHandle.WaitAny( inFlight ) ;
IAsyncResult result = pending[offset] ;
pending.RemoveAt(offset) ;
errorOccurred = resultHandler(result) ;
}
return errorOccurred ;
}
答案 1 :(得分:1)
没有足够的信息来建议你应该采取的方式,但你可能会这样做,那将是任务并行库和Task<T>
。
充满乐趣。但是,不要生气,你最终可能很容易因为你的超级多线程努力比你的同步批处理慢。
如果说T,连接到数据库,向其发出查询,返回一个数据加载器,并说批处理中有8个查询。
您设置任务0到7,启动超时计时器,全部关闭。 完成后,请保留读者并根据任务ID设置一些标志。当它到达255时引发OnBatchComplete事件,复制出你的读者并将它们传递给组合任务。超时首先停止,相应地采取行动。如果任务中出现错误,请让它返回一些合适的原因,冒泡到调用者,可能会杀死任何仍在运行的查询。
不知道你的组合过程是如何运作的,但是如果它可以组织起来那么一旦查询1&amp; 3准备就绪,你可以做一个中间过程,或者如果它是按照某种逻辑顺序,一旦所有的查询都准备好阅读,你是否可以开始阅读简单的类,然后将每一个抛出一个组合任务.... / p>
这不公平我不喜欢这样的东西......
答案 2 :(得分:0)