Die Motivation
begann mit dem Kontakt mit dem Multithread-(Prozess-)Programmiermodell, und was ich lernte, waren die Synchronisationsprimitive im Zusammenhang mit dem Semaphor (Semaphore). Ich weiß nicht, warum es im .Net Framework keine entsprechenden Dinge gibt. Das Schreckliche ist, dass ich sie verwendet habe, um viele bewährte C++-Codes zu implementieren. Um zu verhindern, dass die revolutionären Märtyrer die Medizin vergeblich einnehmen und bluten, musste ich selbst einen zur Welt bringen.
Was ist ein Semaphor?
Wenn Sie das Konzept eines Semaphors bereits verstehen, überspringen Sie bitte diesen Abschnitt.
Semaphor ist eine Funktion, die in einer Multithread-Umgebung verwendet wird. Sie ist für die Koordination verschiedener Threads verantwortlich, um sicherzustellen, dass sie öffentliche Ressourcen korrekt und angemessen nutzen können.
Werfen wir einen Blick darauf, wie ein Parkplatz funktioniert. Nehmen Sie der Einfachheit halber an, dass der Parkplatz nur über drei Parkplätze verfügt und alle drei Parkplätze zu Beginn leer sind. Das heißt, wenn fünf Autos gleichzeitig ankommen und der Pförtner drei von ihnen ungehindert einfahren lässt und dann den Wagenblock aufstellt, müssen die restlichen Autos an der Einfahrt warten, und nachfolgende Autos müssen ebenfalls an der Einfahrt warten Eingang. Zu diesem Zeitpunkt verließ ein Auto den Parkplatz. Nachdem der Pförtner davon erfahren hatte, öffnete er die Autoschranke und stellte ein Auto hinein. Wenn noch zwei Autos wegfuhren, konnte er zwei weitere Autos hineinstellen und so weiter.
In diesem Parkplatzsystem sind Parkplätze öffentliche Ressourcen, jedes Auto ist wie ein Faden und der Pförtner fungiert als Semaphor.
Darüber hinaus sind die Eigenschaften des Semaphors wie folgt: Der Semaphor ist eine nicht negative Ganzzahl (die Anzahl der Parkplätze), und alle Threads (Fahrzeuge), die ihn übergeben, reduzieren die Ganzzahl um eins (die Übergabe verbraucht natürlich Ressourcen). ). Wenn der ganzzahlige Wert Null ist, befinden sich alle Threads, die versuchen, ihn zu übergeben, in einem Wartezustand. Wir definieren zwei Operationen für das Semaphor: Warten und Freigeben. Wenn ein Thread die Wait-Operation aufruft, übergibt er entweder den Semaphor und verringert ihn dann um eins, oder er wartet, bis der Semaphor größer als eins ist oder eine Zeitüberschreitung auftritt. Die Freigabe führt tatsächlich eine Additionsoperation für das Semaphor durch, was dem Verlassen des Parkplatzes durch das Fahrzeug entspricht. Der Grund, warum diese Operation als „Freigabe“ bezeichnet wird, besteht darin, dass die Additionsoperation tatsächlich die durch die Semaphore geschützten Ressourcen freigibt.
erreichen
Wie wir alle wissen, umfassen die in der .Net Framework-Klassenbibliothek bereitgestellten Thread-Synchronisierungsfunktionen:
Monitor, AutoResetEvent, ManualResetEvent, Mutex, ReadWriteLock und InterLock. Unter diesen sind AutoResetEvent, ManualResetEvent und Mutex von WaitHandler abgeleitet und kapseln tatsächlich die vom Betriebssystem bereitgestellten Kernelobjekte. Andere sollten nativ für die virtuelle .Net-Maschine sein. Offensichtlich sind Funktionen von Betriebssystemkernelobjekten weniger effizient zu nutzen. Effizienz ist jedoch kein Thema, das wir hier berücksichtigen müssen. Wir werden zwei Monitore und ein ManualResetEvent-Objekt verwenden, um ein Semaphor zu simulieren.
Der Code lautet wie folgt:
Semaphor der öffentlichen Klasse
{
private ManualResetEvent waitEvent = new ManualResetEvent(false);
privates Objekt syncObjWait = new object();
private int maxCount = 1; //Maximale Anzahl an Ressourcen
private int currentCount = 0; //Die aktuelle Anzahl der Ressourcen
public Semaphore()
{
}
öffentliches Semaphor( int maxCount )
{
this.maxCount = maxCount;
}
public bool Wait()
{
lock( syncObjWait ) //Nur ein Thread kann den folgenden Code eingeben
{
bool waitResult = this.waitEvent.WaitOne(); //Die Anzahl der hier wartenden Ressourcen ist größer als Null
if(waitResult)
{
sperren(dies)
{
if( currentCount > 0 )
{
currentCount--;
if( currentCount == 0 )
{
this.waitEvent.Reset();
}
}
anders
{
System.Diagnostics.Debug.Assert( false, „Semaphore erlaubt keine aktuelle Anzahl < 0“ );
}
}
}
return waitResult;
}
}
/**//// <Zusammenfassung>
/// Wartevorgang, der eine Timeout-Rückgabe ermöglicht
/// </summary>
/// <param name="millisecondsTimeout"></param>
/// <returns></returns>
public bool Wait(int millisecondsTimeout)
{
lock( syncObjWait ) // Monitor stellt sicher, dass sich der Bereichsklassencode innerhalb des kritischen Abschnitts befindet
{
bool waitResult = this.waitEvent.WaitOne(millisecondsTimeout,false);
if(waitResult)
{
sperren(dies)
{
if( currentCount > 0 )
{
currentCount--;
if( currentCount == 0 )
{
this.waitEvent.Reset();
}
}
anders
{
System.Diagnostics.Debug.Assert( false, „Semaphore erlaubt keine aktuelle Anzahl < 0“ );
}
}
}
return waitResult;
}
}
public bool Release()
{
lock( this ) // Monitor stellt sicher, dass sich der Bereichsklassencode innerhalb des kritischen Abschnitts befindet
{
currentCount++;
if( currentCount > this.maxCount )
{
currentCount = this.maxCount;
return false;
}
this.waitEvent.Set(); //Erlaube dem Thread, der Wait aufruft, einzutreten
}
return true;
}
}