web-dev-qa-db-ja.com

SemaphoreSlimの使用方法を理解する必要がある

ここに私が持っているコードがありますが、SemaphoreSlimが何をしているのか理解できません。

_async Task WorkerMainAsync()
{
  SemaphoreSlim ss = new SemaphoreSlim(10);
  List<Task> trackedTasks = new List<Task>();
  while (DoMore())
  {
    await ss.WaitAsync();
    trackedTasks.Add(Task.Run(() =>
              {
                DoPollingThenWorkAsync();
                ss.Release();
              }));
  }
  await Task.WhenAll(trackedTasks);
}

void DoPollingThenWorkAsync()
{
  var msg = Poll();
  if (msg != null)
  {
    Thread.Sleep(2000); // process the long running CPU-bound job
  }
}
_

Await ss.WaitAsync(); & ss.Release();は何をしますか?

一度に50個のスレッドを実行してからSemaphoreSlim ss = new SemaphoreSlim(10);のようなコードを書くと、一度に10個のアクティブなスレッドを実行しなければならないと思います。

10個のスレッドのうちの1つが完了すると、別のスレッドが開始されます。

ss.WaitAsync();と一緒に使用するのにawaitが必要なのはなぜですか? ss.WaitAsync();は何をしますか?

56
Mou

私は一度に50スレッドを実行すると、SemaphoreSlim ss = new SemaphoreSlim(10);一度に10個のアクティブスレッドを強制的に実行します

それは正しいです;セマフォを使用すると、同時にこの作業を行うワーカーが10人を超えないことが保証されます。

セマフォでWaitAsyncを呼び出すと、そのスレッドにそのトークンへの「アクセス」が与えられたときに完了するタスクが生成されます。 await-そのタスクを実行すると、「許可」されたときにプログラムは実行を継続できます。 Waitを呼び出すのではなく非同期バージョンを使用することは、メソッドが同期ではなく非同期のままであることを保証するため、およびasyncメソッドを実行できるという事実に対処するために重要です。コールバックが原因で、複数のスレッドにまたがってコードを作成するため、セマフォとの自然なスレッドアフィニティが問題になる可能性があります。

サイドノート:DoPollingThenWorkAsyncにはAsyncの接尾辞を付けないでください。これは実際には非同期ではなく、同期であるためです。それをDoPollingThenWorkと呼ぶだけです。読者の混乱を軽減します。

48
Servy

この質問は本当にカウントダウンロックのシナリオに関連するものですが、SemaphoreSlimを単純な非同期ロックとして使用したい人のために、このリンクを共有する価値があると考えました。これにより、コーディングを簡潔で安全にするusingステートメントを使用できます。

http://www.tomdupont.net/2016/03/how-to-release-semaphore-with-using.html

__isDisposed=true_と_semaphore.Release()をDisposeの周りでスワップしましたが、どういうわけか複数回呼び出される場合があります。

また、SemaphoreSlimはリエントラントロックではないことに注意することが重要です。つまり、同じスレッドがWaitAsyncを複数回呼び出すと、セマフォのカウントが毎回減少します。要するに、SemaphoreSlimはスレッドを認識しません。

質問のコード品質については、リリースを常に最終的にリリースできるように、リリースを最終的に試してみることをお勧めします。

2
andrew pate