銀の光と碧い空

クラウドなインフラとC#なアプリ開発の狭間にいるエンジニアの日々

PowerShell Cmdlet バイナリモジュールのプラクティスまとめ (1) スレッド・Rx編

今まで何度か、PowerShell Cmdlet バイナリモジュール(C#でdllを記述し、Import-Moduleで利用するCmdlet)について書いてきました。実際、Cmdletを実戦投入して運用していますが、実際に運用してみるとなかなかはまりどころが多いこともわかりました。何回かにわけて、実際にこうやって解決したという(ベストとは言えないけど)プラクティスを紹介したいと思います。

連載目次

  • スレッド・Rx
  • ロギング
  • 例外処理
  • 設定(Configファイル)

スレッド・Rx

いきなりスレッドなわけですが、これは以前下記の記事で紹介したものを修正したいからです。

このエントリでも触れましたが、PowerShell Cmdletは WriteObject メソッドと WriteError メソッドは、呼ばれたメソッド(たとえば、ProcessRecord メソッド)と同じスレッドから実行する必要があります。という観点でみると、実は一つ漏れがあって例外処理時にエラーが発生するのです。

以前載せていたものでは、Rxの Subscribe メソッドのonError の中で例外処理をしていましたが、このAction の中で直接ThrowTerminatingErrorを実行していました。ThrowTerminatingErrorは内部で WriteError メソッドを実行しているのですが、これが呼び出し元スレッドとは別のスレッドで実行されてしまい、Cmdletが強制終了してしまいます。

というわけで例外処理も含めて、すべての WriteObject メソッドと WriteError メソッドをメソッドの呼び出しスレッドと同じスレッドで実行するように修正したのが以下のコードになります。

今回はRxを使ったサンプルになりましたが、 async/await を使った場合やそのほかの別スレッドでの実行を行う処理も同様です。WriteObject メソッドと WriteError メソッドはメソッドの呼び出しスレッドと同じスレッドから呼びだすようにしないといけません。そして、SynchronizationContext.Current がCmdletクラス内では null になっていることにも注意する必要があります。