2011-12-07 13 views
3

私はBackgroundWorkerを使用して、UIが応答しなくなることがないようにバックグラウンドで重いものを実行しています。.Net:バックグラウンドワーカーとマルチプルCPU

しかし、私は私のプログラムを実行すると、2つのCPUのうちの1つだけが使用されていることに気付きました。

使用する方法はありますかすべてBackgroundWorkerのCPUはありますか?ここで

enter image description here

あなたが興味があるだけであれば、私の単純化したコードです!


private System.ComponentModel.BackgroundWorker bwPatchApplier; 

this.bwPatchApplier.WorkerReportsProgress = true; 
this.bwPatchApplier.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bwPatchApplier_DoWork); 
this.bwPatchApplier.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bwPatchApplier_ProgressChanged); 
this.bwPatchApplier.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bwPatchApplier_RunWorkerCompleted); 

private void bwPatchApplier_DoWork(object sender, DoWorkEventArgs e) 
{ 
    string pc1WorkflowName; 
    string pc2WorkflowName; 

    if (!GetWorkflowSettings(out pc1WorkflowName, out pc2WorkflowName)) return; 

    int progressPercentage = 0; 
    var weWorkspaces = (List<WEWorkspace>) e.Argument; 

    foreach (WEWorkspace weWorkspace in weWorkspaces) 
    { 
     using (var spSite = new SPSite(weWorkspace.SiteId)) 
     { 
      foreach (SPWeb web in spSite.AllWebs) 
      { 
       using (SPWeb spWeb = spSite.OpenWeb(web.ID)) 
       { 
        PrintHeader(spWeb.ID, spWeb.Title, spWeb.Url, bwPatchApplier); 

        try 
        { 
         for (int index = 0; index < spWeb.Lists.Count; index++) 
         { 
          SPList spList = spWeb.Lists[index]; 

          if (spList.Hidden) continue; 

          string listName = spList.Title; 

          if (listName.Equals("PC1") || listName.Equals("PC2")) 
          { 
           #region STEP 1 

           // STEP 1: Remove Workflow 

           #endregion 

           #region STEP 2 

           // STEP 2: Add Events: Adding & Updating 

           #endregion 
          } 

          if ((uint) spList.BaseTemplate == 10135 || (uint) spList.BaseTemplate == 10134) 
          { 
           #region STEP 3 

           // STEP 3: Configure Custom AssignedToEmail Property 

           #endregion 

           #region STEP 4 

           if (enableAssignToEmail) 
           { 
            // STEP 4: Install AssignedTo events to Work lists 
           } 

           #endregion 
          } 

          #region STEP 5 

          // STEP 5 Install Notification Events 

          #endregion 

          #region STEP 6 

          // STEP 6 Install Report List Events 

          #endregion 

          progressPercentage += TotalSteps; 
          UpdatePercentage(progressPercentage, bwPatchApplier); 
         } 
        } 
        catch (Exception exception) 
        { 
         progressPercentage += TotalSteps; 
         UpdatePercentage(progressPercentage, bwPatchApplier); 
        } 
       } 
      } 
     } 
    } 

    PrintMessage(string.Empty, bwPatchApplier); 
    PrintMessage("*** Process Completed", bwPatchApplier); 

    UpdateStatus("Process Completed", bwPatchApplier); 
} 

この覗くのおかげで多くのことを:)

答えて

5

BackgroundWorkerは、単一のバックグラウンド(ThreadPool)スレッド内でその作業を行います。したがって、計算上重くなると、1つのCPUが大量に使用されます。 UIスレッドはまだ2番目のスレッドで実行されていますが、おそらく(ほとんどのユーザーインターフェイス作業のように)入力待ちのほとんどすべての時間をアイドル状態で消費しています(これは良いことです)。

複数のCPUを使用するように作業を分割したい場合は、他の手法を使用する必要があります。これは複数のBackgroundWorkerのコンポーネントで、それぞれは何らかの作業をしたり、ThreadPoolを直接使用することができます。並列プログラミングはTPL経由で.NET 4で簡素化されました。これはおそらく非常に良いオプションです。詳細は、my series on the TPLまたはMSDN's page on the Task Parallel Libraryを参照してください。

3

のBackgroundWorkerは、あなたがすることを伝えるものを行うための唯一のシングルスレッドを使用しています。複数のコアを利用するには、複数のスレッドが必要です。つまり、複数のBackgroundWorkersのいずれか、またはDoWorkメソッド内から複数のスレッドを生成することを意味します。

1

BackgroundWorkerは、2番目のCPUコアで新しいスレッドを実行しており、UIは応答しています。

.NET 4を使用している場合は、Task Parallel Libraryを参照してください。これにより、より良い結果が得られ、両方のコアを利用できるようになります。

3

バックグラウンドワーカーは単独で実行スレッドを1つだけ提供します。それは目的をUIのスレッドから取得することです、そして、それはその仕事で非常に良いです。より多くのスレッドが必要な場合は、それらを自分で提供する必要があります。

ここで、SPWebの引数を受け取り、オブジェクトごとにThread.Start()を繰り返し呼び出す方法を作ることが魅力的です。 Thread.Join()またはWaitAll()で終了し、BackgroundWorkerの最後で終了するのを待ちます。しかし、これは悪い考えです。なぜなら、オペレーティングシステムがすべてのスレッド間でコンテキストスイッチを実行する時間を費やすため、効率が低下するからです。

代わりに、システムを少数のスレッドで実行する必要がありますが、少なくとも2つ(この場合は)で実行するようにします。経験則は(2n-1)です。ここでnはプロセッサコアの数ですが、このルールを破るすべての種類のケースがあります。これを実装するには、ThreadPoolを使用するか、SPWebオブジェクトを反復処理し、引き続き引き続きキューに追加するか、TPLなどの手段を使用します。

0

BackgroundWorker自体は、作業を行うためにメインUIとは別に1つのスレッドしか作成していません。そのワークスレッド内の操作を並列化しようとしていません。複数の作業スレッドに作業を分散させる場合は、TPLを使用して調べる必要があります。すべてのタスクがパラレル実行に変換されるわけではないことに注意してください。UIを解放することが唯一の目的なら、これはすでに実行可能な最善の方法です。

0

あり、これに潜在的な落とし穴がありますが、あなたはParallel.ForEachを利用のうち、いくつかの走行距離を取得する可能性があります:

代わりの

foreach (SPWeb web in spSite.AllWebs) 
{ 
    //Your loop code here 
}   

あなたができます

Parallel.Foreach(spSite.AllWebs, web => 
     { 
      //Your loop code here 
     }); 

これは基本的に作成されますTaskPoolで動作する各項目とスケジュールからTask(.NET 4.0のTask APIから)を選択します。これにより、タスクを利用するために必要な並列処理の一部が提供されますeコア。

これから生じる可能性のある必然的な並行性の問題を修正する必要がありますが、それは良い出発点です。スレッド間で共有状態を維持しているという事実を少なくとも修正する予定です(進行カウンタ)。それに関するいくつかのガイダンスがあります:http://msdn.microsoft.com/en-us/library/dd997392.aspx

関連する問題