web-dev-qa-db-fra.com

Besoin de comprendre l'utilisation de SemaphoreSlim

Voici le code que j'ai mais je ne comprends pas ce que fait 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
  }
}

Qu'est-ce que wait ss.WaitAsync(); & ss.Release(); do?

J'imagine que si j'exécute 50 threads à la fois puis que j'écris du code comme SemaphoreSlim ss = new SemaphoreSlim(10);, il sera forcé d'exécuter 10 threads actifs à la fois.

Lorsque l'un des 10 threads est terminé, un autre thread commence ... Si je ne me trompe pas, aidez-moi à comprendre avec un exemple de situation.

Pourquoi faut-il attendre avec ss.WaitAsync();? Que fait ss.WaitAsync();?

56
Mou

je suppose que si je lance 50 threads à la fois, utilisez un code comme SemaphoreSlim ss = new SemaphoreSlim (10); forcera à exécuter 10 threads actifs au moment

C'est correct; l'utilisation du sémaphore garantit qu'il n'y aura pas plus de 10 ouvriers effectuant ce travail en même temps.

L'appel de WaitAsync sur le sémaphore génère une tâche qui sera terminée lorsque ce thread aura "accès" à ce jeton. await- cette tâche permet au programme de continuer l'exécution lorsqu'il est "autorisé" à le faire. Avoir une version asynchrone, plutôt que d'appeler Wait, est important à la fois pour s'assurer que la méthode reste asynchrone, plutôt que d'être synchrone, ainsi que pour tenir compte du fait qu'une méthode async peut être exécutée code sur plusieurs threads, en raison des rappels, et l’affinité naturelle entre les threads et les sémaphores peut donc poser problème.

Remarque: DoPollingThenWorkAsync ne devrait pas avoir le Async postfix car il n’est pas réellement asynchrone, il est synchrone. Appelez-le simplement DoPollingThenWork. Cela réduira la confusion pour les lecteurs.

48
Servy

Bien que j'accepte cette question qui concerne vraiment un scénario de verrouillage du compte à rebours, j'ai pensé qu'il valait la peine de partager ce lien que j'ai découvert pour ceux qui souhaitent utiliser un SemaphoreSlim comme simple verrou asynchrone. Cela vous permet d'utiliser l'instruction using qui pourrait rendre le codage plus net et plus sûr.

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

J'ai échangé _isDisposed=true Et _semaphore.Release() autour de son Dispose bien au cas où il aurait été appelé plusieurs fois.

De plus, il est important de noter que SemaphoreSlim n'est pas un verrou réentrant, ce qui signifie que si le même thread appelle WaitAsync plusieurs fois, le nombre de sémaphores est décrémenté à chaque fois. En bref, SemaphoreSlim n'est pas sensible aux threads.

En ce qui concerne les questions relatives à la qualité du code, il est préférable d’insérer la version dans le but ultime d’essayer-enfin de s’assurer qu’elle soit toujours publiée.

2
andrew pate