search this site:

Chaotic Neutral

左にも右にもよらず、自由な生き方探し。

Windows Powershell ― Start-Job


 Powershell v1には並列処理を実現する仕組みが備わっていなかった。もちろん、.NETのライブラリを駆使して並列処理を実現できないこともなかったが、それは「スクリプティング」という言葉が意味する範疇を超えてしまっていた、と個人的に思います。
 Powershell v2では「Start-Job」というコマンドレットが用意されていて、子スレッドを生成し、並列処理を実現できます。この「Start-Job」は使い方次第では ― というより、普通に使っていれば ― 副作用を伴わない。
 周知のとおり、子スレッドは非同期の呼び出され、戻り値を返さない。通常のメソッドの呼び出しと比べて、子スレッドの処理結果の受け取りには何かと面倒な工夫が必要です。
 子スレッドの処理結果を受け取るありがちなやり方の1つは、親スレッドでオブジェクトを用意し、子スレッドに渡して上書きさせることです。しかし、メソッドの呼び出しの結果、メソッド本体やそれを含むインスタンスのスコープ外の何かが書きかえられること ― いわゆる、副作用 ― には問題があります。スコープ外のオブジェクトの上書きバグの温床になります。また、複数のスレッドがそのオブジェクトを上書きするのであれば、ロックオブジェクトを用いた排他機構が必要です。ロックの習得と解放の間が子スレッドの処理時間の1/100でも、子スレッド数が100あれば、子スレッドの60%以上がロックが解放されるのを待たされることになります。
 もう1つのありがちなやり方は、各スレッドごとにインターフェイスを用意し、スレッドの結果を受け取ることです。これならば副作用は伴わない。しかし、スレッドの処理が終わらなければ、結果を受け取れない。
 Powershell v2の「Start-Job」を使うと、副作用を伴わず、しかも、処理の途中の暫定結果を受け取ることができます。

PS H:\> $c = {$i = 0; while(1){$i++; $i; sleep 1}}
PS H:\> $d = start-job $c
PS H:\> receive-job $d
1
2
3
4
5
6
7
8
9
PS H:\> receive-job $d
10
11
12
13

 「$i」と変数だけを書いた部分が重要です。これは、Pythonの「yield」やC#のIEnumeratorを使った場合の「yield return」と似ていますが、子スレッドは処理をそこで止めません。
 「Receive-Job」を使うと、その時点での処理結果を配列で受け取ることができます。
 Googleの新言語Goでも、並列処理はこんなに簡単ではない。
 同じことがC#でできるようになれば、プログラミング言語の世界で天下を取れそうです。