startコマンドで自分自身のバッチを呼び出す方法でサブルーチンを並列に呼び出す

試した環境

本題

以下の処理ではforループ内で指定した回数(ここでは4回)だけサブルーチンを順次に呼び出しています。 サブルーチンは実際の処理の代わりにtimeoutコマンドを呼んでいます。

@echo off
pushd %~dp0
for /l %%i in (0, 1, 3) do (
    :: call sequentially
    call :subroutine %%i
)
popd
pause

exit /b

:subroutine
echo subroutine(%1)
timeout /t 10
exit /b

このサブルーチンを並列に呼び出したいと思ったとき、startコマンドを使った以下の方法では上手くいきません。

@echo off
pushd %~dp0
for /l %%i in (0, 1, 3) do (
    :: call parallelly?
    start call :subroutine %%i
)
popd
pause

exit /b

:subroutine
echo subroutine(%1)
timeout /t 10
exit /b

上記処理を実行すると、立ち上がったそれぞれのコマンドプロンプトで以下の文章が表示されます。

バッチ スクリプト外でバッチ ラベルを呼び出すことはできません。

そこで強引な方法ですが、自分自身のバッチを呼び出すことでサブルーチンを並列に呼び出します。 以下にスクリプト例を示します。

@echo off

if "%1"=="" (
    pushd %~dp0
    for /l %%i in (0, 1, 3) do (
        :: call parallelly 
        start call "%0" call_myself %%i
    )
    popd
    pause
) else if "%1"=="call_myself" (
    call :subroutine %2
)

exit /b

:subroutine
echo subroutine(%1)
timeout /t 10
exit /b

実装する際には、呼び出し先が再びstartコマンドを呼び出すような無限ループにならないように十分気を付けてください。 私は一度やってしまいました。

並列に立ち上げたコマンドプロンプトを処理終了後に閉じたい場合は、 上記スクリプトstart call "%0" call_myself %%iの行をstart cmd /c "%0" call_myself %%iに書き換えると実現できます。 このことは以下の記事に書きました。

kakashibata.hatenablog.jp