2012-02-10 7 views
2

背景:は、完了したasync sql呼び出しに応答します

データベースに履歴結果をバッチで照会するサービスがあります。 バッチは、開始時刻と終了時刻に基づいています。すべてのバッチ間のデータは相互に排他的です。したがって、バッチのセットでは、任意の順序で実行できます。一部のバッチは他のバッチよりも実行に時間がかかります。いずれかのバッチが失敗した場合は、プロセス全体が中止/中止され、エラーが記録されます。データをクライアントに返すときは、すべてのバッチのデータを結合する必要があります。

問題:

できるだけ早くデータをクライアントに返します。

初期ソリューション:

当初、私は同期の順序でバッチを実行していました。これは、データが相互に排他的であるという事実を利用していませんでした。最初の使用後、このロード方法が長すぎることが判明しました。いくつかのデバッグの後、私は遅いの主な原因がSQLクエリの実行時間であることを発見した。したがって、次の解決策は、BeginExecuteReader()EndExecuteReader()を使用して、各バッチを非同期に実行しようとすることでした。すべてのバッチを非同期に呼び出した後、サービスはwhileループで待機し、300ミリ秒ごとにポーリングして、いずれかのクエリーが完了したかどうかを確認します。そうだったら、それは読まれるでしょう。

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. 完了するとすぐに各バッチを読み始めますか?

更新: このことについて申し訳ありませんが、私は、私は非同期呼び出しを呼び出しているのと同じ方法で返す必要がある部分を追加するのを忘れ。この理由は、データを取り込む必要があるデータセットが、このメソッドのパラメータとして渡されるためです。 begin readerのIAsyncCallbackを使用すると、クラス全体を変更する必要があります。私はそれをする必要はないと思っていた。

+0

どのネットフレームワーク4? –

+0

はい。その.net 4.0 – EndlessSpace

答えて

1

足りない情報をあなたが行くべき道を示唆していますが、かもしれないよっぽど1、それはタスク並列ライブラリとTask<T>だろう。

楽しみの負荷。しかし、怒らないでください。あなたのシンプルなマルチスレッドの努力は、同期バッチよりも遅くなってしまうでしょう。

もしTがdbに接続していたら、それにクエリを投げて、datareaderを返し、バッチで8つのクエリがあったとします。

タスク0〜7を設定し、タイムアウトのためのタイマーを開始し、すべてオフにします。 あなたが完了すると、リーダーを守り、タスクIDに基づいてフラグを少し設定します。 OnBatchCompleteイベントを255にすると、読者をコピーして結合タスクに渡します。タイムアウトが最初に消え、それに応じて行動します。タスクにエラーがある場合は、何らかの適切な理由を返し、呼び出し元にバブルアウトし、実行中のクエリを強制終了させる可能性があります。

結合プロセスの仕組みが分かりませんが、クエリ1 & 3の準備が整ったら、中間プロセスを実行することもできますし、論理的な順序で行うこともできます読みする準備ができている、あなたは、単純なクラスへのへの読み込みを開始し、その後、結合タスクで各1を投げることができ....

それは、私はこのような面白いものを取得しない公正ではない....

0

ポーリングではなく、非同期コールバックを使用する必要があります。 http://msdn.microsoft.com/en-us/library/system.asynccallback.aspx

What is AsyncCallback?

+0

クライアントに戻って結果を渡すことは、おそらくシングルスレッドでなければなりません。あなたのコールバックハンドラは、クライアントにデータを戻すことを試みる2つの競合するコールバックを持たないように、シリアル化する必要があります。 'WaitHandle.WaitAny()'を使うこともできます。 –

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 ; 

} 
関連する問題