2012年12月22日土曜日

PowerShell 3.0 の ForEach –parallel はマルチスレッドではない

PowerShell Advent Calendar 2012 用記事です。

PowerShell 3.0 で追加された PowerShell Workflow の紹介には、かなりの確率で ForEach による並列動作についての記事が掲載されます。ぱっと見ていると、PowerShell でマルチスレッド動作しそうにも思えますが、実際にはどうなのかを検証してみました。


まずはサンプルとして下記のようなスクリプトを作成してみました。

   1:  workflow Test-ParallelWf {
   2:      $numlist = 1..10
   3:      ForEach -parallel ($num in $numlist) {
   4:          $tid = [threading.thread]::CurrentThread.ManagedThreadId
   5:          "Number:{0}  ThreadId:{1}" -f $num, $tid
   6:          Start-Sleep -s 10
   7:          "Complete {0} " -f $num
   8:      }
   9:      "Complete PS-Workflow..."
  10:  }

ForEach で繰り返される処理が、どのスレッドで動作しているか、を取得して表示するスクリプトです。これを実行すると次のような結果になります。

PSWF_1

この通り、実行結果には同一のスレッドIDが出力されているのが見えると思います。これはどういうことなのでしょうか。

   1:          <ns1:PowerShellValue x:TypeArguments="ns3:PSDataCollection(ns3:PSObject)" Expression="$numlist" Result="[ForeachCondition_54385d5b2a13440c87c104e8557c9e39]" />
   2:          <ParallelForEach x:TypeArguments="x:Object">
   3:              <ParallelForEach.Values>
   4:                  <InArgument x:TypeArguments="ns5:IEnumerable(ns0:Object)">
   5:                      <ns1:PowerShellValue x:TypeArguments

先ほどの PowerShell Workflow にて実際に作成される WF4 Workflow の内容を一部抽出したものが上記 xaml です。ParallelForEach アクティビティが利用されているのが見えると思います。ここで思い出してもらいたいのは、Workflow Foundation 4 では Parallel 関係のアクティビティは非同期に動作しない、という点です。挙動としては、シングルスレッド上で Parallel で定義された処理をスケジューリングしておき、Sleep や他の何らかの理由で処理が停滞するタイミングで、継続する処理へとスイッチされるように動作します。ですので一見すると非同期でバシバシ動いてくれそうなのですが、その実まったく非同期ではなくシングルタスク的な動作になってしまいます。

なお、この ParallelForEach アクティビティで処理が切り替わる条件として、MSDN には次のように記載されています。

ただし、スケジュール済みのアクティビティ自体が非同期でない場合 (メッセージング アクティビティ、InvokeMethodAsyncCodeActivity から派生するアクティビティなど) を除き、別々のスレッドでは実行されません。

このあたりを踏まえると、PowerShell Workflow においてもむやみやたらに –parallel させるのではなく、メッセージに関する操作や非同期としてあらかじめ提供されている処理を行うケースなどに限定して利用するのが、効果をあげるポイントになるのではないかと思います。スクリプトを実行しているホスト側の処理をマルチスレッドで動作させよう~、といった場合は特に注意が必要です。今回のサンプルは内部で Start-Sleep を行ったタイミングで処理が切り替わっている、という事になりますので、そういった処理を意図的に切り替えるようなものを仕込んでおくのもいいかも知れません。

そして PowerShell Workflow 全般に関係しているかはわかりませんが、このスケジューリングさせる数にも注意が必要と思われます。MSDN 上では

-MaxSessionsPerWorkflow<Int32>

Specifies the maximum number of session that can be created to support each workflow. The default value is 5.

と書かれており、デフォルトの最大セッション数を 5 としています。実行環境にも左右されるとは思いますが、それ以上スケジューリングさせて動作させるのは、どうやら非効率になる可能性が存在しているようです。

0 件のコメント:

コメントを投稿