首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >并行处理批处理项目

并行处理批处理项目
EN

Stack Overflow用户
提问于 2014-08-18 10:59:42
回答 2查看 1K关注 0票数 6

我有一个ItemGroup,并希望并行处理它的所有项(使用自定义任务或.exe)。

  • 我可以编写任务/exe来接受整个ItemGroup,并在内部并行地处理它的项。但是,我希望这种并行性与MSBuild的/maxCpuCount param一起工作,否则我可能会导致过度并行化。
  • 这个帖子说没有办法。
  • 我的测试表明,MSBuild的/maxCpuCount只适用于构建不同的项目,而不是项目(参见下面的代码)

如何并行处理来自ItemGroup的项?

是否有一种方法可以将定制任务与MSBuild的并行支持并行工作?

代码语言:javascript
运行
复制
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Target Name="Build" >
    <!-- Runs only once - I guess MSBuild detects it's the same project -->
    <!--<MSBuild Projects="$(MSBuildProjectFullPath);$(MSBuildProjectFullPath)" Targets="Wait3000" BuildInParallel="true" />-->

    <!-- Runs in parallel!. Note that b.targets is a copy of the original a.targets -->
    <MSBuild Projects="$(MSBuildProjectFullPath);b.targets" Targets="Wait3000" BuildInParallel="true" />

    <!-- Runs sequentially -->
    <ItemGroup>
      <Waits Include="3000;2000"/>
    </ItemGroup>
    <Wait DurationMs="%(Waits.Identity)" />
  </Target>

  <Target Name="Wait3000">
    <Wait DurationMs="3000" />
  </Target>

  <UsingTask TaskName="Wait" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
    <ParameterGroup>
      <DurationMs ParameterType="System.Int32" Required="true" />
    </ParameterGroup>
    <Task>
      <Code Type="Fragment" Language="cs">
        Log.LogMessage(string.Format("{0:HH\\:mm\\:ss\\:fff}  Start  DurationMs={1}", DateTime.Now, DurationMs), MessageImportance.High);
        System.Threading.Thread.Sleep(DurationMs);
        Log.LogMessage(string.Format("{0:HH\\:mm\\:ss\\:fff}  End    DurationMs={1}", DateTime.Now, DurationMs), MessageImportance.High);
      </Code>
    </Task>
  </UsingTask>
</Project>   
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-03-17 21:18:02

我知道这是老生常谈,但如果您有几分钟的时间,请重新考虑使用MSBuild任务的尝试。使用Properties和/或AdditionalProperties保留项元数据元素*将解决您在代码示例中描述的问题(“只运行一次--我猜MSBuild检测到的是同一个项目”)。

下面的MSBuild文件通过MSBuild的并行支持(包括/maxCpuCount)并行处理来自ItemGroup的项。它不使用来自BuildTargetsInParallel扩展包的MSBuild,也不使用任何其他自定义或内联任务。

代码语言:javascript
运行
复制
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <Target Name="Build" >
    <ItemGroup>
      <Waits Include="3000;2000"/>
    </ItemGroup>

    <ItemGroup>
      <ProjectItems Include="$(MSBuildProjectFullPath)">
        <Properties>
          WaitMs=%(Waits.Identity)
        </Properties>
      </ProjectItems>
    </ItemGroup>
    <MSBuild Projects="@(ProjectItems)" Targets="WaitSpecifiedMs" BuildInParallel="true" />
  </Target>

  <Target Name="WaitSpecifiedMs">
    <Wait DurationMs="$(WaitMs)" />
  </Target>

</Project>

*隐藏在MSBuild任务参考页面上的“属性元数据”下。

票数 7
EN

Stack Overflow用户

发布于 2014-08-18 12:54:33

就像你自己说的,您不能在目标或任务级别上并行化,但您可以放弃

我的自定义任务大量使用TPL并行化,即我的基本任务包装器有一个ForEach包装器。

代码语言:javascript
运行
复制
public bool ForEach<T>(IEnumerable<T> enumerable, Action<T> action, int max = -1)
{
    return enumerable != null && Parallel.ForEach(enumerable, new ParallelOptions { MaxDegreeOfParallelism = max }, (e, s) =>
    {
        if (Canceled)
            s.Stop();
        if (s.ShouldExitCurrentIteration)
            return;
        action(e);
        Interlocked.Increment(ref _total);
    }).IsCompleted;
}

通常限制是由.NET本身省略和管理的,很少有像MSDeploy这样的非线程安全操作,部署配置DoS限制为20的配置DoS报告,或者如果超过CPU计数1,压缩任务就会严重退化。可能不值得阅读maxCpuCount并使用Environment.ProcessorCount%NUMBER_OF_PROCESSORS%,但您可以尝试解析命令行或在主机对象上进行反映,例如我的基本任务类有此方法来获取各种额外特殊全局标志的所有属性、目标等。

代码语言:javascript
运行
复制
private void Engine(object host)
{
    var type = host.GetType();
    if (type.FullName != "Microsoft.Build.BackEnd.TaskHost")
    {
        Log.Warn("[Host] {0}", type.AssemblyQualifiedName);
        return;
    }

    var flags = BindingFlags.NonPublic | BindingFlags.Instance;
    var taskLoggingContext = type.GetProperty("LoggingContext", flags).GetValue(host, null);
    var targetLoggingContext = taskLoggingContext.GetType().GetProperty("TargetLoggingContext", flags).GetValue(taskLoggingContext, null);

    ProjectTask = taskLoggingContext.GetType().GetProperty("Task", flags).GetValue(taskLoggingContext, null).To<ProjectTaskInstance>();
    ProjectTarget = targetLoggingContext.GetType().GetProperty("Target", flags).GetValue(targetLoggingContext, null).To<ProjectTargetInstance>();

    var entry = type.GetField("requestEntry", flags).GetValue(host);
    var config = entry.GetType().GetProperty("RequestConfiguration").GetValue(entry, null);

    Project = config.GetType().GetProperty("Project").GetValue(config, null).To<ProjectInstance>();
    Properties = Project.Properties.ToDictionary(p => p.Name, p => p.EvaluatedValue);

使用ForEach,典型的任务看起来会像这样

代码语言:javascript
运行
复制
public class Transform : Task
{
    [Required]
    public ITaskItem[] Configs { get; set; }

    protected override void Exec()
    {
        //...

        ForEach(Configs, i =>
        {
            //...
        }, Environment.ProcessorCount);

        //...
    }
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25361719

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档