平行

說明

平行執行巢狀任務,不保證執行緒安全性。每個任務將在自己的執行緒中執行,同時執行問題發生的機率會隨著主機系統的 CPU 數量而增加。

警告:雖然 Apache Ant 核心被認為是執行緒安全的,但任務並未在 Ant 的測試過程中針對執行緒安全性進行測試,因此無法提供此類保證。第三方任務可能執行緒安全,也可能不安全,而 Ant 的某些核心任務(例如 <javac>)絕對不是可重入的。這是因為它們使用從未設計為在多執行緒環境中使用的函式庫。

<parallel> 的主要使用案例是同時執行外部程式(例如應用程式伺服器)和 JUnit 或 TestNG 測試組。任何嘗試同時執行大型 Ant 任務序列(例如 javadocjavac)的人,都必須明確承擔找出並修正他們執行的任務中所有並行錯誤的任務。

因此,雖然此任務有其用途,但應視為進階任務,應在某些批次處理或測試情況下使用,而不是在多核心 CPU 上加快建置時間的簡易技巧。

參數

屬性 說明 必要
threadCount 要使用的執行緒最大數量。
threadsPerProcessor 每個可用處理器要使用的執行緒最大數量 (Java 1.4 以上) 否;遞延至 threadCount
timeout 執行終止前的毫秒數
failonany 如果任何巢狀任務失敗,任務執行將在該點完成,而不會等到任何其他任務完成。 否;預設為 false
pollInterval 目前沒有任何效果 否;預設為 1000

平行任務在 Ant 建置檔案中有多種用途,包括

任何有效的 Ant 任務都可以嵌入在 parallel 任務中,包括其他 parallel 任務,儘管無法保證任務在這種環境中會執行緒安全。

parallel 任務中的任務執行期間,主執行緒將會被封鎖,等待所有子執行緒完成。如果執行在逾時或嵌套任務失敗時終止,且 failonany 旗標已設定,則 parallel 任務將會完成,而不會等待其他執行緒中的其他嵌套任務完成。

如果 <parallel> 任務中的任何任務失敗,且 failonany 未設定,則其他執行緒中的剩餘任務將會繼續執行,直到所有執行緒都已完成。在這種情況下,parallel 任務也會失敗。

parallel 任務可以與 sequential 任務結合,以定義要在平行區塊中的每個執行緒上執行的任務順序。

threadCount 屬性可用於設定執行可用的最大執行緒數。當不存在時,所有子任務將會一次執行。當存在時,同時執行的最大任務數不會超過指定的執行緒數。此外,每個任務將會按其給定的順序啟動。但是,不保證執行速度或任務完成順序,只保證每個任務會在下一任務之前啟動。

如果您使用的是 Java 1.4 或更新版本,您也可以使用 threadsPerProcessor,可用的執行緒數將會是處理器數的開始倍數(然而,沒有與特定處理器的關聯性)。這將會覆寫 threadCount 中的值。如果在任何舊版 JVM 上指定 threadsPerProcessor,則 threadCount 中的值將會按原樣使用。

在使用 threadCountthreadsPerProcessor 時,應小心確保建置不會陷入僵局。這可能是因為 waitfor 等任務在會解除 waitfor 的任務發生之前就佔用了所有可用的執行緒。這並非 Java 語言層級執行緒語意的替代方案,最適合用於「極度平行」的任務。

指定為嵌套元素的參數

daemons

parallel 任務支援一個 <daemons> 巢狀元素。這是一個清單,其中包含要平行執行在守護執行緒中的任務。parallel 任務不會等待這些任務完成。但是,由於是守護執行緒,它們不會阻止 Ant 完成,並終止執行緒。在 parallel 任務本身完成之前,守護執行緒中發生的失敗會被報告,並可能導致 parallel 擲出例外。parallel 完成後發生的失敗不會被報告。

例如,守護任務可以用於啟動測試伺服器,而這些伺服器可能無法從 Ant 輕鬆終止。透過使用 <daemons>,此類伺服器不會停止建置。

範例

這是測試伺服器應用程式的典型模式。在一個執行緒中,伺服器會啟動 (<wlrun> 任務)。另一個執行緒包含三個按順序執行的任務。<sleep> 任務用於讓伺服器有時間啟動。可以將有能力驗證伺服器是否可用的另一個任務用於取代 <sleep> 任務。<junit> 測試架構隨後會在它自己的 JVM 中執行。一旦測試完成,伺服器就會停止 (在此範例中使用 <wlstop>),允許兩個執行緒完成。<parallel> 任務也會在此時完成,然後建置會繼續進行。

<parallel>
  <wlrun ... >
  <sequential>
    <sleep seconds="30"/>
    <junit fork="true" forkmode="once" ... >
    <wlstop/>
  </sequential>
</parallel>

在此,兩個獨立任務執行以在建置期間達成更好的資源利用。在此範例中,一些 servlet 在一個執行緒中編譯,而一組 JSP 在另一個執行緒中預編譯。開發人員需要小心,這兩個任務在它們的相依性方面和在 Ant 外部環境中它們的潛在互動方面都是獨立的。在此,我們設定 <javac> 任務的 fork=true,以便它在一個新程序中執行;如果 <wljspc> 任務在 VM 中使用 javac 編譯器 (它可能使用),可能會出現並行問題。

<parallel>
  <javac fork="true"...> <!-- compiler servlet code -->
  <wljspc ...> <!-- precompile JSPs -->
</parallel>

此範例代表使用 threadCountthreadsPerProcessor 屬性的典型需求。同時執行所有 40 個工作可能會讓系統的記憶體和 CPU 時間癱瘓。透過限制同時執行的數量,您可以減少 CPU、記憶體和磁碟 I/O 的競爭,因此實際上可以更快完成。這也是使用 threadCount(以及可能使用 threadsPerProcessor)的良好候選,因為每個工作都是獨立的(每個新的 JVM 都會分岔)且不依賴於其他工作。

 <macrodef name="dbpurge">
    <attribute file="file"/>
   <sequential>
      <java jar="utils/dbpurge.jar" fork="true" >
        <arg file="@{file}/>
      </java>
   </sequential>
</macrodef>

<parallel threadCount="4">
  <dbpurge file="db/one"/>
  <dbpurge file="db/two"/>
  <dbpurge file="db/three"/>
  <dbpurge file="db/four"/>
  <dbpurge file="db/five"/>
  <dbpurge file="db/six"/>
  <dbpurge file="db/seven"/>
  <dbpurge file="db/eight"/>
  <!-- repeated about 40 times -->
</parallel>