2011-12-07 20 views
3

我使用BackgroundWorker在后台执行一些繁重的工作,以便UI不会变得无法响应。.Net:Background Worker和多个CPU

但今天我注意到,当我运行我的程序时,只有两个CPU中的一个正在使用。

有没有什么办法可以使用全部具有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)线程中完成其工作。因此,如果它的计算量很大,它将大量使用一个CPU。 UI线程仍然在第二个线程上运行,但可能(像大多数用户界面工作)花费几乎所有的时间空闲等待输入(这是一件好事)。

如果您想分割您的工作以使用多个CPU,则需要使用其他一些技术。这可能是多个BackgroundWorker组件,每个组件都在做一些工作,或者直接使用ThreadPool。并行编程已经在.NET 4中通过TPL进行了简化,这可能是一个非常好的选择。详情请参阅my series on the TPLMSDN's page on the Task Parallel Library

3

每个BackgroundWorker的只使用一个线程做的东西,你告诉它做。要利用多个内核,您需要多个线程。这将意味着多个BackgroundWorkers或从您的DoWork方法中产生多个线程。

1

BackgroundWorker正在第二个CPU内核上运行一个新线程,从而使UI响应。

如果您使用.NET 4,请使用Task Parallel Library进行调查,这可以为您提供更好的结果并利用两个内核。

3

背景工作者本身只提供一个额外的执行线程。它的目的是从UI线程中解脱出来,并且非常擅长这项工作。如果你想要更多的线程,你需要自己提供它们。

在这里很容易建立一个接受SPWeb参数的方法,并为每个对象反复调用Thread.Start();然后使用Thread.Join()或WaitAll()完成,以等待它们在BackgroundWorker的末尾完成。但是,这将是一个糟糕的主意,因为操作系统花费时间在所有线程之间执行上下文切换,从而导致效率下降。相反,你想强制你的系统只在少数线程中运行,但至少有两个(在这种情况下)。一个好的经验法则是(2n-1),其中“n”是你拥有的处理器内核的数量......但是在各种情况下,你想打破这个规则。您可以通过使用ThreadPool,迭代SPWeb对象并将它们添加到您不断从其中拉取的队列或其他方法(如TPL)来实现此目的。

0

BackgroundWorker本身只是创建一个独立于主UI的线程来完成工作 - 它不会尝试在该工作线程中并行化操作。如果您想将工作分散到多个工作线程中,则应使用TPL进行研究。请记住,并非所有任务都能很好地转化为并行执行,所以如果释放UI是你唯一的目标,那么这可能已经是你所能做到的最好的了。

0

对于这个潜在的缺陷,但你可能会得到一些里程出利用的Parallel.ForEach:

而不是

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

,你可以:

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

这基本上创建任务(来自.NET 4.0中的任务API)来自每个项目和与TaskPool一起工作的时间表,这将为您提供一些您需要利用的并行机制e核心。

您将不得不解决可能由此产生的不可避免的并发问题,但这是一个很好的起点。您至少要解决您在线程之间维护共享状态的事实(进度计数器)。以下是一些指导:http://msdn.microsoft.com/en-us/library/dd997392.aspx