在完成时响应异步sql调用

时间:2012-02-10 20:59:12

标签: c# multithreading concurrency

背景:

我有一个服务,它会分批查询数据库中的历史结果。 批次基于开始时间和结束时间。所有批次之间的数据是互斥的,因此对于给定的批次集合,它们可以按任何顺序运行。有些批次可能比其他批次花费更多时间。如果任何批次失败,则中止/停止整个过程并记录错误。将数据返回给客户端时,需要组合所有批次的数据。

问题:

尽快将数据返回给客户端。

初步解决方案:

最初我是按顺序同步执行批次的。这并没有利用数据相互排斥的事实。初次使用后,发现这种加载方法耗时太长。经过一些调试,我发现缓慢的主要原因是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);
    }
}

上述方法减少了大型数据集的加载时间,但是对于非常小的数据集(但是在许多批次中),轮询实际上增加了加载延迟。

问题:

  1. 如何异步执行sql,但不进行轮询?
  2. 一旦完成,就开始阅读每个批次?
  3. 更新: 对此抱歉,但我忘了在调用异步调用的同一方法中添加我需要返回的部分。这样做的原因是我需要填充的数据集作为参数传递给此方法。使用begin reader中的IAsyncCallback将需要我更改整个类。我希望我不必这样做。

3 个答案:

答案 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)