私は次のコードを持っています:
using (Mutex mut = new Mutex(false, MUTEX_NAME))
{
if (mut.WaitOne(new TimeSpan(0, 0, 30)))
{
// Some code that deals with a specific TCP port
// Don't want this to run at the same time in another process
}
}
if
ブロック内にブレークポイントを設定し、Visual Studioの別のインスタンス内で同じコードを実行しました。予想どおり、.WaitOne
呼び出しはブロックします。しかし、驚いたことに、最初のインスタンスでcontinueになり、using
ブロックが終了するとすぐに、放棄されたミューテックスに関する2番目のプロセスで例外が発生します。
修正はReleaseMutex
を呼び出すことです:
using (Mutex mut = new Mutex(false, MUTEX_NAME))
{
if (mut.WaitOne(new TimeSpan(0, 0, 30)))
{
// Some code that deals with a specific TCP port
// Don't want this to run twice in multiple processes
}
mut.ReleaseMutex();
}
今、物事は期待どおりに機能します。
私の質問:通常IDisposable
のポイントはそれですcleans upどんな状態に入れても構いません。おそらく複数のwaitsおよびreleasesはusing
ブロック内にありますが、Mutexのハンドルが破棄されると、自動的に解放されるべきではありませんか?つまり、ReleaseMutex
ブロックにいる場合にusing
を呼び出す必要があるのはなぜですか?
また、if
ブロック内のコードがクラッシュした場合、ミューテックスが破棄されることを心配しています。
Mutex
ブロックにusing
を入れることには利点がありますか?または、単にnew up a Mutex
インスタンスをtry/catchにラップし、finallyブロック内でReleaseMutex()
を呼び出す必要があります(基本的に私thoughtDispose()
が行うことを正確に実装します)
ドキュメンテーション は、instantiatingMutexの間に概念的な違いがあることを(「解説」セクションで)説明しています。オブジェクト(これはnotを実行し、実際には同期が行われる限り特別なことを行います)およびacquiringMutex( WaitOne
)。ご了承ください:
WaitOne
はブール値を返します。つまり、Mutexを取得するとfail(タイムアウト)になり、両方のケースを処理する必要があります。WaitOne
がtrue
を返す場合、呼び出しスレッドはMutexを取得しており、must call ReleaseMutex
を取得していない場合、Mutexは破棄されます。false
を返す場合、呼び出しスレッドする必要はありません call ReleaseMutex
したがって、インスタンス化だけでなく、ミューテックスにもあります。とにかくusing
を使用する必要があるかどうかについては、Dispose
が何をするのかを見てみましょう( から継承されるWaitHandle
):
_protected virtual void Dispose(bool explicitDisposing)
{
if (this.safeWaitHandle != null)
{
this.safeWaitHandle.Close();
}
}
_
ご覧のとおり、Mutexはnotでリリースされていますが、何らかのクリーンアップが含まれているため、using
を使用することをお勧めします。
続行方法については、もちろん_try/finally
_ブロックを使用して、Mutexが取得された場合に適切に解放されるようにすることができます。これはおそらく最も簡単なアプローチです。
really Mutexの取得に失敗した場合(TimeSpan
をWaitOne
に渡すため指定していない)を気にしない場合は、独自のクラスでMutex
をラップできます。 IDisposable
を実装し、コンストラクターでMutexを取得し(引数なしでWaitOne()
を使用して)、Dispose
内で解放します。ただし、これはお勧めしません。これは、何か問題が発生した場合にスレッドを無期限に待機させ、取得の試行時に両方のケースを明示的に処理する 正当な理由 、@ HansPassantで述べたように。
この設計上の決定は、ずいぶん前になされました。 21年以上前、.NETが構想されるか、IDisposableのセマンティクスが考慮されるずっと前に。 .NET Mutexクラスは、ミューテックスの基盤となるオペレーティングシステムサポート用のwrapperクラスです。コンストラクターは CreateMutex をpinvokesし、WaitOne()メソッドは WaitForSingleObject() をpinvokesします。
WaitForSingleObject()のWAIT_ABANDONED戻り値に注意してください。これが例外を生成するものです。
Windows設計者は、mutexmustを所有するスレッドが終了する前にReleaseMutex()を呼び出すという堅固なルールを定めました。そして、これがスレッドが予期しない方法で、通常は例外を介して終了したという非常に強力な兆候であるということではない場合。これは、同期が失われることを意味します。これは非常に深刻なスレッド化バグです。同じ理由で.NETのスレッドを終了させる非常に危険な方法であるThread.Abort()と比較してください。
.NETデザイナーは、この動作を一切変更しませんでした。少なくとも、待機を実行する以外にミューテックスの状態をテストする方法がないためです。あなたはReleaseMutex()を呼び出す必要があります。また、2番目のスニペットも正しくないことに注意してください。取得していないミューテックスで呼び出すことはできません。 if()ステートメント本体内に移動する必要があります。
ミューテックスの主な用途の1つは、不変条件を満たさない状態の共有オブジェクトを見る唯一のコードが、オブジェクトをその状態にする(できれば一時的に)コードであることを保証することです。オブジェクトを変更する必要があるコードの通常のパターンは次のとおりです。
#2の開始後、#3の終了前に何か問題が発生した場合、オブジェクトは不変条件を満たさない状態のままになることがあります。適切なパターンは、ミューテックスを解放する前にミューテックスを解放することなので、コードがミューテックスを解放せずに処分するという事実は、どこかで問題が発生したことを意味します。そのため、(リリースされていないので)コードがミューテックスに入るのは安全ではないかもしれませんが、ミューテックスがリリースされるのを待つ理由はありません(破棄されているため、決して破棄されないため) 。したがって、適切なアクションは例外をスローすることです。
.NETミューテックスオブジェクトによって実装されるパターンよりもやや優れたパターンは、「取得」メソッドに、ミューテックスではなく、その特定の取得をカプセル化するIDisposable
オブジェクトを返すことです。そのオブジェクトを破棄すると、ミューテックスが解放されます。コードは次のようになります。
using(acq = myMutex.Acquire())
{
... stuff that examines but doesn't modify the guarded resource
acq.EnterDanger();
... actions which might invalidate the guarded resource
... actions which make it valid again
acq.LeaveDanger();
... possibly more stuff that examines but doesn't modify the resource
}
EnterDanger
とLeaveDanger
の間で内部コードが失敗した場合、取得オブジェクトはDispose
を呼び出してmutexを無効にする必要があります。他の場所で内部コードが失敗した場合、保護されたリソースは有効な状態であるため、ミューテックスを解放する必要があり、using
ブロック内のコードはこれにアクセスする必要がなくなります。そのパターンを実装するライブラリの特定の推奨事項はありませんが、他の種類のミューテックスのラッパーとして実装することは特に難しくありません。
OK、私の質問への回答を投稿します。私が知ることができることから、thisはMutex
を実装する理想的な方法です:
WaitOne
が成功した場合に限り、リリースされます。これが誰かの助けになることを願っています!
_using (Mutex mut = new Mutex(false, MUTEX_NAME))
{
if (mut.WaitOne(new TimeSpan(0, 0, 30)))
{
try
{
// Some code that deals with a specific TCP port
// Don't want this to run twice in multiple processes
}
catch(Exception)
{
// Handle exceptions and clean up state
}
finally
{
mut.ReleaseMutex();
}
}
}
_
更新:try
ブロック内のコードがリソースを不安定な状態にした場合、notMutexをリリースし、代わりに放棄させます。言い換えると、コードが正常に終了したときにmut.ReleaseMutex();
を呼び出すだけで、finally
ブロック内に配置しないでください。 Mutexを取得するコードは、この例外をキャッチし、正しいことを行います。
私の状況では、私は本当に状態を変えていません。私は一時的にTCPポートを使用しています。プログラムの別のインスタンスを同時に実行することはできません。このため、上記のソリューションは問題ないと思いますが、異なる場合があります。
MSDNページの最初に何が起こっているのかを知るには、.netよりも多くを理解する必要があります。
interprocess同期にも使用できる同期プリミティブ。
MutexはWin32“Named Object”であり、各プロセスはそれをロックしますby name、. netオブジェクトはWin32呼び出しの単なるラッパーです。 Muxtex自体は、アプリケーションのアドレス空間ではなく、Windows Kernalアドレス空間内に存在します。
ほとんどの場合、単一プロセス内のオブジェクトへのアクセスのみを同期しようとしている場合は、 Monitor を使用する方が適切です。
ミューテックスが解放されることを保証する必要がある場合は、try catch finallyブロックに切り替えて、mutexリリースをfinallyブロックに入れます。ミューテックスの所有者であり、ハンドルを持っていることを前提としています。リリースが呼び出される前に、そのロジックを含める必要があります。
注意:ガベージコレクションプロセスはWindowsによるハンドルを所有していないため、ガベージコレクタによって実行されるMutex.Dispose()は失敗します。
ReleaseMutex
のドキュメントを読むと、Mutexを意識的にリリースする必要があるという設計上の決定があったようです。 ReleaseMutex
が呼び出されない場合、保護セクションの異常終了を意味します。リリースを最終的にまたは破棄することで、このメカニズムを回避します。もちろん、AbandonedMutexExceptionを無視してもかまいません。
Dispose
は、リリースされるWaitHandle
に依存します。したがって、using
はDispose
を呼び出しますが、安定状態の条件が満たされるまで影響を及ぼしません。 ReleaseMutex
を呼び出すと、リソースを解放していることをシステムに伝えているため、自由に廃棄できます。