
■ 要件
powerShellで作ったとある処理を、サーバーのログオン時に実行させています。
テスト段階では問題なく動作していたんですが、実運用を開始したところ、時々異常終了するようになりました。
原因を調べたところ、そのサーバーにリモートデスクトップで接続(ログイン)したせいで、powerShellの処理が2重起動したことが原因でした。
サーバーの遠隔操作ソフトはあるので、リモートデスクトップで接続しない運用にしても良いのですが、この手の運用ルールの追加は、大体ろくな結果にならない。
なんとか2重起動を防げないだろうか?
■ サンプル
ミューテックスという仕組みを利用すれば、2重起動を防げそうです。
10秒待機して終了するだけの単純な処理を作成して、そこにミューテックスを追加します。
Write-Output ('処理を開始します。 ' + (Get-Date -format yyyyMMdd_HHmmss))
# ミューテックスを作成します。
# 他ユーザーからの実行もあり得るので、「Global¥」を付けてグローバル・ミューテックスを利用します。
$mutexObject = New-Object System.Threading.Mutex($false, "Global¥MUTEX_TEST")
# シグナルを受信します。戻値がFalseの場合、処理を終了します。
# ※ Falseの場合、別のスレッドでミューテックスが作成されています。
if (-not $mutexObject.WaitOne(0, $false)) {
Write-Warning "既に処理が実行されています。"
Read-Host '何かキーを押すと、処理を終了します。'
exit
}
# 10秒待機します。
Start-Sleep -Seconds 10
# ミューテックスを解放します。
$mutexObject.ReleaseMutex()
# ミューテックスのリソースを解放します。
$mutexObject.Close()
# 終了メッセージ
Write-Output ('処理を終了します。 ' + (Get-Date -format yyyyMMdd_HHmmss))
exit
# -------------------------
# 前回の異常終了検出処理
# -------------------------
trap [System.Threading.AbandonedMutexException] {
Write-Warning "前回の処理で強制終了が発生しています。処理は続行されます。"
continue
}
解説
ミューテックスの使用方法の概要は、ミューテックスオブジェクトの作成、シグナル確認、ミューテックスオブジェクトの解放・廃棄になります。
1.ミューテックスオブジェクトの作成
New-Objectコマンドレットで、インスタンスを作成します。
作成するのは、System.Threading.Mutexクラス(.NET Frameworkのオブジェクト)。
コンストラクタの第一引数は、False固定、第二引数に適当な名前を付けときます。
この時に、先頭を「Global¥」とプレフィックスを付けることでシステム全体を対象とした2重起動の抑止が可能になります。要件が「リモートデスクトップでログインされた時の2重起動抑止」なので、これが無いときちんと動きませんでした。
2.シグナルを受信
WaitOneメソッドの戻値から、2重起動を検出します。
このメソッドの本来の役割は、「他のスレッドが使用している資産が解放されるまで待機する」で、戻値にTrueが返却された場合は、資産を使用できる状態になったということです。
第一引数は待機時間です、今回は待機する必要はないので0でOK。
第二引数は、マニュアル読んだんですけど、意味が分かりませんでした。
今回の用途では、Falseを設定しておけば良いようです。
このメソッドの戻値がFalseになる、ということは、既に処理が実行されているということですので、2重起動の警告メッセージを表示して処理を終了させます。
3.後始末
作成したミューテックスを解放します。
ReleaseMutexで解放、Closeで廃棄です。
このあたりは、オブジェクトを使用した時のお約束ですよね。
4.前回の異常の検出
powerShellが項番3の後始末をしない状態で終了された場合、次回の起動時にWaitOneメソッドで例外(AbandonedMutexException)が発生します。
十分にあり得る話なので、エラートラップを仕込んで、処理を続行できるようにしています。
■ 実験開始
まず、単独で実行してみます。
通常時は問題なく動作するようです。

では、前回異常時のエラートラップを実験してみます。
待機処理の実行中にCtrl + C で処理を終了させ、再度処理を実行します。

一見、上手く動作したように思えたのですが、思ったようには動いてくれませんでした。
例えば、powerShellの実行画面を2つ起動して、片方で処理を実行して途中で終了させます。
この状態で、もう一つの画面で同じ処理を実行したところ、前回異常時のエラーメッセージは表示されませんでした。
...なんでだろ?まぁ、前回異常時も処理を続行させるのが目的なので、ここでは目をつぶります。
最後に、本命の2重起動のチェックをしてみます。
画面を2つ同時に起動して、片方の画面で実行中に、もう片方でも実行します。

うむ、2つ目の処理が動きませんね。これで行けそうです。
投稿記事の一覧:http://harikofu.web.fc2.com/
--- blog end ---
スポンサードリンク


