Semaphor auf C ++ - Ereignissen

Heute werde ich kurz darauf eingehen, wie ich ein Semaphor basierend auf dem Synchronisationsobjekt „Event“ implementiert habe.

Gehen Sie zuerst die Definitionen durch.

1. Was ist Synchronisation und warum wird sie benötigt?


Natürlich können wir eine Reihe von Aktionen auf verschiedene Arten ausführen. Am einfachsten - sequentiell und parallel. Die parallele Implementierung bestimmter Aktionen kann durch Ausführen verschiedener Threads erreicht werden. Die Idee ist einfach: Weisen Sie jedem Thread eine elementare (oder keine) Aktion zu und führen Sie sie in einer bestimmten Reihenfolge aus. Im Allgemeinen können wir sie alle gleichzeitig starten - natürlich werden wir einen Zeitgewinn erzielen. Dies ist verständlich: Es ist eine Sache, 10.000 Wörter nacheinander auszugeben, und eine andere Sache, beispielsweise 100 Wörter gleichzeitig auszugeben. 100-facher Zeitgewinn (plus oder minus, ausgenommen Verzögerungen usw.). Die ursprüngliche Aufgabe kann jedoch eine strenge Abfolge von Aktionen beinhalten.

Zum Beispiel:

  • Datei öffnen
  • Schreiben Sie Text in eine Datei
  • Datei schließen

Das Gewächshaus-Beispiel wurde speziell genommen (es ist klar, dass hier keine Parallelität erforderlich ist, alles kann einfach nacheinander ausgeführt werden), aber als Trainingsaufgabe wird es vollständig funktionieren, und am wichtigsten ist, dass die Notwendigkeit einer konsequenten Ausführung deutlich an seinem Beispiel erkennbar ist. Oder hier ist ein anderes Beispiel, etwas anders:

  • Erzeugen Sie drei Folgen von Zufallszahlen
  • Zeigen Sie sie nacheinander an

Hier kann der erste Punkt gleichzeitig von drei verschiedenen Threads ausgeführt werden, aber die letzte Schlussfolgerung muss nacheinander und erst nach dem Ausarbeiten des ersten Punkts erfolgen.

Im Allgemeinen können Parallelitätsaufgaben sehr unterschiedlich sein, und zum Synchronisieren von Threads sind einige Tools erforderlich.

2. Tools für die Thread-Synchronisation


Windows.h implementiert eine ganze Reihe von regulären Synchronisationswerkzeugen (die sogenannten "Synchronisationsobjekte"). Unter den wichtigsten sind: kritische Region, Ereignis, Mutex, Semaphor. Ja, für Semaphore gibt es bereits eine Implementierung in windows.h. "Warum also programmieren?" Sie fragen. Nun, erstens, um ein besseres Gefühl dafür zu bekommen, wie es funktioniert. Und zweitens hat das zusätzliche Üben von C ++ noch niemanden aufgehalten :)

Da wir Events verwenden werden, werden wir erklären, was es ist und wie es verwendet wird.

Ereignisse werden verwendet, um ausstehende Threads zu benachrichtigen. Dies ist in der Tat ein Signal für den Fluss - es kann ausgelöst werden oder noch nicht. Aus der eigentlichen Bedeutung dieses Objekts folgt, dass es einen bestimmten Signalzustand aufweist und angepasst werden kann (Zurücksetzen / Einschalten).

Nach dem Verbinden von windows.h können wir also ein Ereignis erstellen mit:

HANDLE CreateEvent ( LPSECURITY_ATTRIBUTES lpEventAttributes, //   BOOL bManualReset, //  : TRUE -  BOOL bInitialState, //  : TRUE -  LPCTSTR lpName //   ); 

Wenn die Funktion erfolgreich ist, wird das Ereignis-Handle zurückgegeben. Wenn das Objekt nicht erstellt werden konnte, wird NULL zurückgegeben.

Um den Status des zu signalisierenden Ereignisses zu ändern, verwenden wir die Funktion:

 BOOL SetEvent ( HANDLE hEvent //   ); 

Bei Erfolg wird ein Wert ungleich Null zurückgegeben.

Nun zum Semaphor. Das Semaphor steuert die Anzahl der gleichzeitig ausgeführten Threads. Angenommen, wir haben 1000 Threads, aber nur 2 können gleichzeitig arbeiten. Dies ist die Art der Anpassung, die mit Hilfe eines Semaphors erfolgt. Und welche Funktionen sind implementiert, um mit diesem Synchronisationsobjekt zu arbeiten?

So erstellen Sie ein Semaphor, ähnlich wie bei Ereignissen:

 HANDLE CreateSemaphore ( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, //   LONG lInitialCount, //     LONG lMaximumCount, //    LPCTSTR lpName //   ); 

Wenn erfolgreich, bekommen wir einen Zeiger auf das Semaphor, wenn es fehlschlägt, bekommen wir NULL.

Der Semaphorenzähler ändert sich ständig (der Thread läuft und eine freie Stelle wird angezeigt), sodass der Status des Semaphors regelmäßig geändert werden muss. Dies geschieht mit dieser Funktion:

 BOOL ReleaseSemaphore ( HANDLE hSemaphore, //    LONG lReleaseCount, //     LPLONG lpPreviousCount //   ); 

Bei Erfolg ist der Rückgabewert ungleich Null.

Beachten Sie auch die Funktion:

 DWORD WaitForSingleObject( HANDLE hHandle, //   ,     DWORD dwMilliseconds //     ); 

Von den zurückgegebenen Werten interessiert uns insbesondere 2: WAIT_OBJECT_0 - bedeutet, dass der Zustand unseres Objekts signalisiert wird; WAIT_TIMEOUT - Wir haben in der zugewiesenen Zeit nicht auf den Signalzustand des Objekts gewartet.

3. Die Aufgabe selbst


Insgesamt besteht unsere Aufgabe darin, unsere Analoga über reguläre Funktionen zu schreiben. Wir werden die Aufgabe nicht sehr komplizieren, wir werden "Umsetzung in erster Näherung" machen. Die Hauptsache ist, die quantitativen Eigenschaften des Standardsemaphors beizubehalten. Code mit Kommentaren finden Sie auf GitHub .

Aufgrund der Einfachheit der Aufgabe selbst werden wir den Artikel nicht besonders komplizieren, aber es kann für jemanden nützlich sein :)

Source: https://habr.com/ru/post/de476940/


All Articles