【问题标题】:Parallel MSBUILD - critical sections?并行 MSBUILD - 关键部分?
【发布时间】:2013-03-27 08:14:46
【问题描述】:

我的组织有一些在构建服务器上运行的大型构建,构建了大量与 ProjectReferences 链接的 MSBUILD 项目。我们需要能够与msbuild /m 并行构建项目和配置。

我的问题是我有一个项目被大量其他项目引用,但该项目本身是不可可重入的。如果超过两个或更多节点尝试并行构建该项目,它将失败。

如何将这个项目或它的目标包装在关键部分中?

我真正需要做的是这样的事情:

<Target>
    <EnterCriticalSection ID=$(ProjectGuid) />
    <Exec something />
    <LeaveCriticalSection ID=$(ProjectGuid) />
</Target>

这个想法是,如果多个 MSBUILD 节点尝试并行构建此项目,则只有一个节点可以执行,其余节点必须等待(或去做其他事情)。

我想我可以编写自定义的 MSBUILD 任务来执行此操作,但是 MSBUILD 系统中没有内置的方法吗?

=== 编辑 4/5/13。澄清一下,该项目正在使用为其提供的构建脚本构建一个 3rd-party 库。完全重写他们的构建脚本以使其可重入——通过确保每个构建对中间文件使用不同的文件夹集等——在理论上是可能的,但不是一个实际的解决方案。一方面,所有这些工作都必须在该库的每个新版本上重做。

=== 编辑 4/6/13。进一步思考,我认为甚至理论上不可能确保项目是可重入的。让我解释一下:

假设项目 XYZ 设置为根据平台和配置以通常的方式使用不同的临时目录:
XYZ.proj

<PropertyGroup>
  <MyWorkingDir>tmp.$(Platform).$(Configuration)</MyWorkingDir>
</PropertyGroup>

现在假设一些其他项目 GraphicsWindow 通过 ProjectReferences 或 MSBuild 任务引用项目 XYZ。并假设可以构建 GraphicsWindow 项目以使用各种图形 API。即,有一个OpenGL版本,一个DirectX 9版本,一个DirectX 10版本......

所以在某个地方有一个 .proj 或 .targets 文件,其中包含构建所有四个版本的目标:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <ItemGroup>
        <ProjectToBuild Include="GraphicsWindow.proj">
            <Properties>GraphicsApi=OpenGL</Properties>
        </ProjectToBuild>
        <ProjectToBuild Include="GraphicsWindow.proj">
            <Properties>GraphicsApi=D3D9</Properties>
        </ProjectToBuild>
        <ProjectToBuild Include="GraphicsWindow.proj">
            <Properties>GraphicsApi=D3D10</Properties>
        </ProjectToBuild>
        <ProjectToBuild Include="GraphicsWindow.proj">
            <Properties>GraphicsApi=D3D11</Properties>
        </ProjectToBuild>
    </ItemGroup>
    <Target Name="All">
        <MSBuild Projects="@(ProjectToBuild)" BuildInParallel="true" />
    </Target>
</Project>

或使用批处理的等效项。

现在 MSBuild 将使用 same 平台|配置组合和相同的工作目录构建 XYZ 项目 4 次。

只要您在没有 /m 选项的情况下构建并且 MSBuild 运行单个线程,这将正常工作。根据 XYZ 项目的编写方式,第 2 次、第 3 次和第 4 次构建可能什么都不做,因为输出是最新的,或者它可能会做一些多余的工作,但最终结果将是正确的并且构建会成功.

但是一旦你开始使用并行 MSBuild,这个构建就被破坏了!现在有一个竞争条件,多个线程可以同时进入 XYZ 项目的目标,并开始使用相同的工作目录,这将失败。

【问题讨论】:

  • 您确定需要并行构建多个项目吗? Msbuild 已经并行并同时编译多个源文件。由于硬盘访问瓶颈,尝试并行执行多个构建可能会导致性能下降。

标签: msbuild


【解决方案1】:

无论您如何执行 MSBuild,无论是否使用 multi-proc 选项 /m,都可以保证为构建请求的每个配置执行一次项目。这是MSDN的引述:

当 Microsoft 构建引擎在使用并行构建来构建项目时遇到项目到项目 (P2P) 引用时,它只会构建一次引用。如果两个项目具有相同的 P2P 引用,则不会为每个项目重新构建该引用。相反,构建引擎为依赖它的两个项目返回相同的 P2P 引用。会话中对同一目标的未来请求将提供相同的 P2P 参考。

如果您看到多次构建同一个项目,这意味着它在两个(或多个)不同的配置中被引用。这里的配置是指传递给项目的一组参数,例如项目平台(x86、x64、AnyCPU 等)风格(调试/零售)、本地化语言、您可能使用的任何其他参数。

这通常是项目平台混合的问题。例如,您有为 x64 构建的项目 A,为 AnyCPU 构建的项目 B,并且 A 和 B 都引用 C。现在必须为 x64 和 AnyCPU 构建两次 C。如果 C 通过将输出清晰地分开到单独的目录中正确处理这两个平台,则没有问题。但是,如果 C 将 x64 和 AnyCPU 视为相同,它将在多进程构建中随机失败。

首先检查您的解决方案配置对话框。确保所有项目都具有一致的平台/配置参数集。如果您需要在不同的配置中构建相同的项目,请确保将输出放在不同的位置。

【讨论】:

  • 我确定您是对的,Seva,并且每次都使用不同的配置参数构建有问题的项目,但这有什么帮助?该项目正在使用自己的构建脚本构建第 3 方库。我想理论上我可以完全重写他们的构建命令以始终将中间文件放在不同的位置,但这似乎不是一个实际的解决方案。
  • 我认为真正的问题是您希望如何构建第 3 方项目——您希望它被构建多次(以不同的配置)还是只希望它构建一次。在我的回答中,我暗示只想一次构建它。
  • 如果只建一次,能不能重入也没关系,我也不会问这个问题。
  • 您不能在多个配置中构建一次项目。例如,如何在一次调用中构建 x86 和 x64 二进制文件?如果你想要两种架构,你必须构建它两次。如果您不需要多个配置,请确保在所有引用的相同配置中调用项目。
  • 我不明白你最后的评论。
猜你喜欢
  • 2020-10-30
  • 1970-01-01
  • 1970-01-01
  • 2014-02-07
  • 2014-04-20
  • 1970-01-01
  • 1970-01-01
  • 2020-01-15
  • 2015-07-25
相关资源
最近更新 更多