車輪の再発明に長い時間を費やす前に、.Net に目的の機能を実行するクラスが既に存在するかどうかを確認したいと思いました。
私が欲しいのは、セマフォに少し似たもの (またはおそらくカウントダウンイベントに似たもの) ですが、少し異なります。
さまざまな数の「リソース」を利用できるという要件があり、利用可能なリソースがゼロのときにスレッドを効率的に待機させたいと考えています。その間、別のスレッドがリソースを解放できます。これにより、待機中の他のスレッドがすぐに解放されます。
これはセマフォによく似ていますが、セマフォが(私が見る限り)各スレッドをカウントするという点で「リソース」として扱うためではありません。
とにかく、これが私が望むものの最初の簡単な実装です。破棄、コード コントラクト、エラー処理、タイムアウトのサポート、またはキャンセルのサポートはまだありませんが、私が望むものを示す必要があります。
public sealed class ResourceCounter
{
/// <summary>Create with the specified number of resources initially available.</summary>
public ResourceCounter(int resourceCount)
{
_resourceCount = resourceCount;
if (_resourceCount > 0)
{
_resourceAvailable.Set();
}
}
/// <summary>Acquires a resource. Waits forever if necessary.</summary>
public void Acquire()
{
while (true)
{
_resourceAvailable.Wait();
lock (_lock)
{
if (_resourceCount > 0)
{
if (--_resourceCount == 0)
{
_resourceAvailable.Reset();
}
return;
}
}
}
}
/// <summary>Releases a resource.</summary>
public void Release()
{
lock (_lock)
{
++_resourceCount;
_resourceAvailable.Set();
}
}
private int _resourceCount;
private readonly object _lock = new object();
private readonly ManualResetEventSlim _resourceAvailable = new ManualResetEventSlim();
}
使用パターンは非常に単純です。
必要な初期リソース カウント (ゼロ以上) で ResourceCounter を構築します。
リソースを取得したいスレッドは、ResourceCounter.Acquire() を呼び出します。これは、リソースが利用可能になり、取得されるまで返されません。
リソースを解放したいスレッドは ResourceCounter.Release() を呼び出します。これはリソースを解放してすぐに戻ります。
どのスレッドでもリソースを解放できることに注意してください。リソースを取得したものである必要はありません。
これをマルチスレッド パイプライン コードの一部として使用しています。1 つのスレッドが作業項目のエンキューを担当し、複数のスレッドが作業項目を処理し、別のスレッドが処理された作業項目を出力しています。処理された作業項目を出力するスレッドはそれらを多重化する必要があり (処理スレッドは完了した項目を任意の順序で出力する可能性があるため)、マルチプレクサが遅れた項目を待機している間、作業項目が際限なくキューに入れられるのを停止するメカニズムが必要でした。
(これに関する背景については、パイプライン、多重化、および無制限のバッファリングを参照してください。)
とにかく、これを行うために既に利用できるものはありますか、それとも独自のクラスを開発し続ける必要がありますか?
[編集]
以下に示すように、SemaphoreSlim は正確に正しいことを行います。Wait() を呼び出したスレッドは Release() を呼び出したスレッドでなければならないと思ったので拒否しましたが、そうではありませんでした。これは私が日曜日にコーディングするために得たものです... ;)