2017-02-01 15 views
0

私は小さなジレンマを持っています。私は、独自の接続文字列(同じか異なる可能性があります)を持つデータセット内のクエリのリストを調べ、すべてのDataTablesを埋め込み、すべてのクエリを埋め込んだ塗りつぶしデータセットを返す関数を持っています。c#マルチスレッドデータベースの塗りつぶしテーブル

現在このプロセスは1つずつ実行されるため、クエリーの1つに10分かかり、残りの3つのクエリーにそれぞれ2分かかると、実行時間は16分になります。

この状況でマルチスレッドを使用できるかどうかは疑問でした。これは別のスレッドでFillTableをすべて呼び出す必要があり、実行時間を10分に短縮する必要があります。これらは、明示的にFill DataTable呼び出しのみです(更新または削除呼び出しはありません)。

これは私がこれまでに得たものである:

public void FillDataSet(ref DataSet Source) 
    { 
     foreach (var r in Source.Tables["queries"].Rows) 
     { 
      string query = r["QueryStatement"].ToString(); 
      string qSource = r["QuerySource"].ToString(); 
      string tableName = r["TableName"].ToString(); 

      DBConnection db = new DBConnection(); 
      var TempSource = Source; 
      taskConnection = Task.Factory.StartNew(() => callDB(db, query, tableName, ref TempSource)); 
      Source = TempSource; 
     } 
     Task.WaitAll(taskConnection); 
    } 

    private void callDB(DBConnection db, string query, string tableName, ref DataSet Source) 
    { 
     using (var sql = new SqlConnection(db.ConnectionString)) 
     { 
      sql.Open(); 
      using (var adp = new SqlDataAdapter(query, sql)) 
      { 
       adp.SelectCommand.CommandTimeout = 0; 
       adp.Fill(Source, tableName); 
      } 
     } 
    } 

ラムダ式は、(私はこれを変更することはできません)パラメータの参照を渡して好きではないので、私はTempSourceを作成する必要がありました。現在のところ、これは動作しません、私は間違っているか分からない。

+0

**ジレンマ**は、2つの同等に悪い(または同等に良い)結果の間の選択です。あなたはジレンマを持っていないように思えます。解決したい問題だけです。 – Enigmativity

+1

いずれにしても、データベース呼び出しがIOであり、スレッドがメモリ内のデータに対して最適に動作するため、スレッド化でパフォーマンスが向上する可能性はほとんどありません。既存のコードをスピードアップし、スレッド化が見えない状況を改善するかどうかを尋ねない方法があれば、本当に尋ねているはずです。 – Enigmativity

+0

私はクエリを制御する必要はありません。私は各スレッドが同時に独自のクエリを実行して、各クエリが完了するのを本質的に待たないようにします。たとえば、スレッドごとに5分かかる5つのクエリがある場合、これは5分で25分以内に完了する必要があります。 –

答えて

1

ここには、使用できる基本的な定型文があります。 - 新しいスレッドを産卵のオーバーヘッドを作業量に比べて最小限であるので、私が代わりにスレッドプールのスレッドを使用しました

// Set your connectionstring and execute the query and fill your data here 

これは基本的なものです: は、私がコメントを残してきたビットを入力してください。スレッドを追跡し、スレッド信号などを使用して、より高度な振る舞いを実装することで、これを拡張することができます。

さらに、作業を行うコードに余分なパラメータを渡す場合は、これらを作業項目定義クラスに追加します。

注:これは、主なRunParallelメソッドの複数の並列実行をサポートしていませんが、簡単に拡張することができます。

public static class RunParallel 
{ 
    const int NumThreadsToRunInParallel = 8;// Tune this for your DB server performance characteristics 
    public static void FillDataSet(ref DataSet Source) 
    { 
     WorkItemDefinition Work; 
     foreach (DataRow r in Source.Tables["queries"].Rows) 
     { 
      Work = new WorkItemDefinition(); 
      Work.Query = r["QueryStatement"].ToString(); 
      Work.QSource = r["QuerySource"].ToString(); 
      Work.TableName = r["TableName"].ToString(); 
      EnQueueWork(Work); 
     } 
     System.Threading.ThreadStart NewThreadStart; 
     NewThreadStart = new System.Threading.ThreadStart(ProcessPendingWork); 
     for (int I = 0; I < NumThreadsToRunInParallel; I ++) 
     { 
      System.Threading.Thread NewThread; 
      NewThread = new System.Threading.Thread(NewThreadStart); 
      //NewThread.IsBackground = true; //Do this if you want to allow the application to quit before these threads finish all their work and exit 
      ThreadCounterInc(); 
      NewThread.Start(); 
     } 
     while (ThreadCounterValue > 0) 
     { 
      System.Threading.Thread.Sleep(1000); 
     } 
    } 

    private static void ProcessPendingWork() 
    { 
     try 
     { 
      WorkItemDefinition Work; 
      Work = DeQueueWork(); 
      while (Work != null) 
      { 
       Work = DeQueueWork(); 
       DbConnection db = new OdbcConnection(); 
       // Set your connectionstring and execute the query and fill your data here 
      } 
     } 
     finally 
     { 
      ThreadCounterDec(); 
     } 
    } 

    private static int ThreadCounter = 0; 
    private static void ThreadCounterInc() 
    { 
     lock(SyncRoot) 
     { 
      ThreadCounter += 1; 
     } 
    } 
    private static void ThreadCounterDec() 
    { 
     lock (SyncRoot) 
     { 
      ThreadCounter -= 1; 
     } 
    } 
    private static int ThreadCounterValue 
    { 
     get 
     { 
      lock (SyncRoot) 
      { 
       return ThreadCounter; 
      } 
     } 
    } 

    private static object SyncRoot = new object(); 
    private static Queue<WorkItemDefinition> m_PendingWork = new Queue<WorkItemDefinition>(); 
    private static Queue<WorkItemDefinition> PendingWork 
    { 
     get 
     { 
      return m_PendingWork; 
     } 
    } 

    private static WorkItemDefinition DeQueueWork() 
    { 
     lock (SyncRoot) 
     { 
      if (PendingWork.Count > 0) // Catch exception overhead is higher 
      { 
       return PendingWork.Dequeue(); 
      } 
     } 
     return null; 
    } 

    private static void EnQueueWork(WorkItemDefinition Work) 
    { 
     lock (SyncRoot) 
     { 
      PendingWork.Enqueue(Work); 
     } 
    } 

    public class WorkItemDefinition 
    { 
     public string Query { get; set; } 
     public string QSource { get; set; } 
     public string TableName { get; set; } 
    } 
} 
+0

それはうまくいった!どうもありがとう! –

+0

@ civic.sirようこそ。好奇心からどれくらいのスピードアップが得られましたか?最終的には物理IOとハッシュバケットのような内部SQLリソースがハードな制限です。 SQLはディスク記憶層のIO並行性レベル、特に高速アレイとSSDを常に知っているとは限らないことにも注意してください。したがって、複数のファイルにまたがってテーブルをパーティション化することで、さらにパフォーマンスを調整することができます。これにより、SQLは個別のIOスレッドを使用します。したがって、SQLが最大になってもパフォーマンスモニタでディスクに十分な空き容量がある場合は、この方法を試すことができます。 – tcwicks

+0

この実装の前にクエリはそれほど素晴らしいものではなく、同期プロセスなのでアプリケーションを実行するのに約45分かかりました。この実装の後、15分のパフォーマンスが大幅に向上しました。ありがとうございました –

関連する問題