La motivation
est partie du contact avec le modèle de programmation multi-thread (processus), et ce que j'ai appris, ce sont les primitives de synchronisation liées au sémaphore (Semaphore). Je ne sais pas pourquoi il n'y a pas d'éléments correspondants dans .Net Framework. Le plus terrible, c'est que je l'ai utilisée pour implémenter de nombreux codes C++ éprouvés. Afin d'empêcher les martyrs révolutionnaires de prendre des médicaments et de saigner en vain, j'ai dû en donner un moi-même.
Qu'est-ce qu'un sémaphore ?
Si vous comprenez déjà le concept de sémaphore, veuillez ignorer cette section.
Le sémaphore est une fonctionnalité utilisée dans un environnement multithread. Il est chargé de coordonner les différents threads pour garantir qu'ils peuvent utiliser les ressources publiques correctement et raisonnablement.
Jetons un coup d'œil au fonctionnement d'un parking. Par souci de simplicité, supposons que le parking ne dispose que de trois places de stationnement et que les trois places de stationnement sont vides au début. C'est le cas si cinq voitures arrivent en même temps et que le portier permet à trois d'entre elles d'entrer sans entrave, puis dépose le bloc de voitures, les voitures restantes doivent attendre à l'entrée, et les voitures suivantes devront également attendre à l'entrée. entrée. À ce moment-là, une voiture a quitté le parking. Après que le gardien l'ait appris, il a ouvert la barrière pour voiture et a mis une voiture à l'intérieur. Si deux voitures supplémentaires partaient, il pourrait y mettre deux voitures supplémentaires, et ainsi de suite.
Dans ce système de parking, les places de stationnement sont des ressources publiques, chaque voiture est comme un fil et le gardien agit comme un sémaphore.
De plus, les caractéristiques du sémaphore sont les suivantes : le sémaphore est un entier non négatif (le nombre de parkings), et tous les threads (véhicules) le passant réduiront l'entier de un (le passer, c'est bien sûr utiliser des ressources ). Lorsque la valeur entière est à zéro, tous les threads essayant de la transmettre seront dans un état d'attente. Nous définissons deux opérations sur le sémaphore : Wait et Release. Lorsqu'un thread appelle l'opération Wait, soit il réussit puis décrémente le sémaphore de un, soit il attend que le sémaphore soit supérieur à un ou expire. Release effectue en fait une opération d'ajout sur le sémaphore, qui correspond à la sortie du véhicule du parking. La raison pour laquelle cette opération est appelée "release" est que l'opération d'ajout libère effectivement les ressources gardées par le sémaphore.
accomplir
Comme nous le savons tous, les fonctionnalités de synchronisation de threads fournies dans la bibliothèque de classes .Net Framework incluent :
Monitor, AutoResetEvent, ManualResetEvent, Mutex, ReadWriteLock et InterLock. Parmi eux, AutoResetEvent, ManualResetEvent et Mutex sont dérivés de WaitHandler et encapsulent en fait les objets du noyau fournis par le système d'exploitation. D'autres doivent être natifs de la machine virtuelle .Net. De toute évidence, les fonctionnalités des objets du noyau du système d'exploitation sont moins efficaces à utiliser. Cependant, l'efficacité n'est pas un problème que nous devons considérer ici. Nous utiliserons deux Monitors et un objet ManualResetEvent pour simuler un sémaphore.
Le code est le suivant :
Sémaphore de classe publique
{
private ManualResetEvent waitEvent = new ManualResetEvent(false);
objet privé syncObjWait = new object();
private int maxCount = 1; //Nombre maximum de ressources
private int currentCount = 0; //Le nombre actuel de ressources
public Semaphore()
{
}
Sémaphore public (int maxCount )
{
this.maxCount = maxCount;
}
public bool Attendez()
{
lock( syncObjWait ) //Un seul thread peut saisir le code suivant
{
bool waitResult = this.waitEvent.WaitOne(); //Le nombre de ressources en attente ici est supérieur à zéro
si (attendre le résultat)
{
verrouiller (ceci)
{
si (compte courant > 0)
{
currentCount--;
si (compte courant == 0)
{
this.waitEvent.Reset();
}
}
autre
{
System.Diagnostics.Debug.Assert( false, "Le sémaphore n'autorise pas le nombre actuel < 0" );
}
}
}
retourner waitResult ;
}
}
/**//// <résumé>
/// Opération d'attente qui permet le retour du délai d'attente
/// </summary>
/// <param name="millisecondsTimeout"></param>
/// <retours></retours>
public bool Attendre (int millisecondsTimeout)
{
lock( syncObjWait ) // Monitor garantit que le code de la classe de portée se trouve dans la section critique
{
bool waitResult = this.waitEvent.WaitOne(millisecondsTimeout,false);
si (attendre le résultat)
{
verrouiller (ceci)
{
si (compte courant > 0)
{
currentCount--;
si (compte courant == 0)
{
this.waitEvent.Reset();
}
}
autre
{
System.Diagnostics.Debug.Assert( false, "Le sémaphore n'autorise pas le nombre actuel < 0" );
}
}
}
retourner waitResult ;
}
}
public bool Release()
{
lock(this) // Le moniteur garantit que le code de la classe de portée se trouve dans la section critique
{
currentCount++;
if( currentCount > this.maxCount )
{
currentCount = this.maxCount;
renvoie faux ;
}
this.waitEvent.Set(); //Autoriser le thread appelant Wait à entrer
}
renvoie vrai ;
}
}