マルチスレッドアプリケーションにメソッドがあり、このメソッドが呼び出されたときに次の動作が必要です。
- 他のスレッドが現在メソッドを実行していない場合は、それを実行します。
- 別のスレッドが現在メソッドを実行している場合は、実行せずにメソッドを終了します。
C#のlock
ステートメントは、スレッドが実行を完了するまで待機するのに役立ちますが、このメソッドへのアクセスをシリアル化するのではなく、別のスレッドによって実行されている場合は、このメソッドの実行をバイパスします。
マルチスレッドアプリケーションにメソッドがあり、このメソッドが呼び出されたときに次の動作が必要です。
C#のlock
ステートメントは、スレッドが実行を完了するまで待機するのに役立ちますが、このメソッドへのアクセスをシリアル化するのではなく、別のスレッドによって実行されている場合は、このメソッドの実行をバイパスします。
これは、Monitor.TryEnterを使用して実行できますが、おそらくもっと簡単です。インターロック:
int executing; // make this static if you want this one-caller-only to
// all objects instead of a single object
void Foo() {
bool won = false;
try {
won = Interlocked.CompareExchange(ref executing, 1, 0) == 0;
if(won) {
// your code here
}
} finally {
if(won) Interlocked.Exchange(ref executing, 0);
}
}
私は理解していないと思います...一度に1つのスレッドだけによって呼び出される必要がある場合、なぜ複数のスレッドが最初にそれを呼び出すのですか?
とにかく、 Monitor.TryEnter()を使用できます。ブロックせずfalse
、ロックの取得に失敗した場合は戻ります。その場合は、関数から戻ることができます。
別の場所にbool変数を作成し、メソッドの開始時にtrueに設定し、メソッドの終了時にfalseに設定します。メソッドを実行する前に、変数がfalseかどうかを確認してから、メソッドから終了します。
この目的のためのヘルパーメソッドは次のとおりです。
static class Throttle
{
public static void RunExclusive(ref int isRunning, Action action)
{
if (isRunning > 0) return;
bool locked = false;
try
{
try { }
finally
{
locked = Interlocked.CompareExchange(ref isRunning, 1, 0) == 0;
}
if (locked) action();
}
finally
{
if (locked)
Interlocked.Exchange(ref isRunning, 0);
}
}
}
次のように使用します。
private int _isTuning = 0;
private void Tune() { ... }
...
Throttle.RunExclusive(ref _isTuning, Tune);