Cross Process Event - Geben Sie alle Kellner zuverlässig frei

8

Ich habe ein Cross-Process-Event über ManualResetEvent erstellt. Wenn dieses Ereignis eintritt, sollten möglicherweise n Threads in n verschiedenen Prozessen entsperrt werden und gestartet werden, um die neuen Daten abzurufen. Das Problem ist, dass ManualResetEvent.Set, gefolgt von einem sofortigen Reset, nicht dazu führt, dass alle wartenden Threads aufwachen. Die Dokumente sind dort ziemlich vage.

Ссылка

  

Wenn der Status eines manuell zurücksetzbaren Ereignisobjekts signalisiert wird, bleibt es bestehen   signalisiert, bis sie explizit auf Nicht-Melden durch das ResetEvent zurückgesetzt wird   Funktion. Beliebige Anzahl von wartenden Threads oder Threads, die anschließend   Beginne Wartetätigkeiten für das angegebene Ereignisobjekt, kann freigegeben werden   während der Zustand des Objekts signalisiert wird.

Es gibt eine Methode namens PulseEvent Das scheint genau das zu tun, was ich brauche, aber leider ist es auch fehlerhaft.

  

Ein Thread, der auf ein Synchronisationsobjekt wartet, kann vorübergehend sein   aus dem Wartezustand von einem Kernel-Modus-APC entfernt und dann zu zurückgegeben   der Wartezustand nach dem APC ist abgeschlossen. Wenn der Anruf zu PulseEvent   tritt während der Zeit auf, wenn der Thread aus dem Wartezustand entfernt wurde   Status, der Thread wird nicht freigegeben, da PulseEvent veröffentlicht wird   nur die Threads, die im Moment des Aufrufs warten.   Daher ist PulseEvent unzuverlässig und sollte nicht von neuen verwendet werden   Anwendungen. Verwenden Sie stattdessen Bedingungsvariablen.

Jetzt empfiehlt MS, Zustandsvariablen zu verwenden.

  

Zustandsvariablen sind Synchronisationsgrundelemente, die Threads aktivieren   zu warten, bis eine bestimmte Bedingung auftritt. Zustandsvariablen sind   Objekte im Benutzermodus, die nicht über Prozesse hinweg gemeinsam genutzt werden können .

Nach den Unterlagen scheint mir das Pech fehlgeschlagen zu sein, um es zuverlässig zu machen. Gibt es eine einfache Möglichkeit, dasselbe ohne die angegebenen Einschränkungen mit einem ManualResetEvent zu erreichen, oder muss ich für jeden Listener-Prozess ein Antwort-Ereignis erstellen, um ein ACK für jeden abonnierten Anrufer zu erhalten? In diesem Fall würde ich einen kleinen gemeinsamen Speicher benötigen, um die PIDs der abonnierten Prozesse zu registrieren, aber das scheint seine eigenen Probleme mit sich zu bringen. Was passiert, wenn ein Prozess abstürzt oder nicht reagiert? ....

Um etwas Kontext zu geben. Ich habe einen neuen Status zu veröffentlichen, der alle anderen Prozesse von einem freigegebenen Speicherort lesen sollte. Es ist in Ordnung, ein Update zu verpassen, wenn mehrere Aktualisierungen gleichzeitig durchgeführt werden, aber der Prozess muss mindestens den letzten aktuellen Wert lesen. Ich konnte mit einer Zeitüberschreitung abfragen, aber das scheint keine richtige Lösung zu sein.

Momentan bin ich bei

%Vor%     
Alois Kraus 06.05.2013, 06:35
quelle

3 Antworten

4

Eine allgemeine Möglichkeit zur Behandlung des Falles, in dem die Erzeuger alle Verbraucher wecken müssen und die Zahl der Verbraucher sich entwickelt, ist der Einsatz eines beweglichen Zauns. Diese Option erfordert ebenfalls eine gemeinsame IPC-Region. Die Methode führt manchmal dazu, dass Verbraucher geweckt werden, wenn keine Arbeit vorhanden ist, insbesondere wenn viele Prozesse geplant werden müssen und die Last hoch ist, aber sie werden immer wieder aufwachen, außer auf hoffnungslos überladenen Maschinen.

Erstellen Sie mehrere manuelle Reset-Ereignisse, und lassen Sie die Produzenten einen Zähler für das nächste Ereignis einrichten, das festgelegt wird. Alle Ereignisse werden mit Ausnahme des NextToFire-Ereignisses festgelegt. Consumer-Prozesse warten auf das NextToFire-Ereignis. Wenn der Hersteller alle Konsumenten wecken möchte, setzt er das Ereignis Next + 1 zurück und setzt das aktuelle Ereignis. Alle Verbraucher werden schließlich geplant und warten dann auf das neue NextToFire-Ereignis. Der Effekt ist, dass nur der Produzent ResetEvent verwendet, aber die Konsumenten wissen immer, welches Ereignis als nächstes sie wecken wird.

All Users Init: (Pseudocode ist C / C ++, nicht C #)

%Vor%

Produzent, um alle zu wecken

%Vor%

Verbraucher warten auf Logik

%Vor%     
rlb 30.04.2015, 02:44
quelle
2

Seit .NET 4.0 können Sie mit MemoryMappedFile Prozessspeicher synchronisieren. In diesem Fall schreiben Sie den Zähler in MemoryMappedFile und dekrementieren Sie ihn von Worker-Prozessen. Wenn der Zähler gleich Null ist, kann der Hauptprozess das Ereignis zurücksetzen. Hier ist der Beispielcode.

Hauptprozess

%Vor%

WorkerProcess

%Vor%

Wenn die Umgebung kleiner ist als .NET 4.0, können Sie dies mithilfe der Funktion "CreateFileMapping" aus der win32-API realisieren.

    
Darksanta 07.05.2013 03:14
quelle
0

Sie haben geschrieben: "PulseEvent, das genau das zu tun scheint, was ich brauche, aber leider ist es auch fehlerhaft". Das stimmt, dass PulseEvent fehlerbehaftet ist, aber ich kann nicht zustimmen, dass das manuelle Zurücksetzen fehlerhaft ist. Es ist sehr zuverlässig. Es gibt Fälle, in denen Sie Ereignisse manuell zurücksetzen können, und in einigen Fällen können Sie sie nicht verwenden. Es ist kein Ein-für-allemal. Es gibt viele andere Tools, wie Auto-Reset-Ereignisse, Pipes, etc.

Die beste Möglichkeit, einen Thread nur dann zu benachrichtigen, wenn Sie ihn regelmäßig benachrichtigen müssen, aber keine Daten über Prozesse hinweg senden müssen, ist ein Ereignis zum automatischen Zurücksetzen. Sie brauchen nur ein eigenes Ereignis für jeden Thread. Sie haben also so viele Ereignisse wie es Threads gibt.

Wenn Sie nur Daten an Prozesse senden müssen, ist es besser, Named Pipes zu verwenden. Im Gegensatz zu Ereignissen mit automatischer Zurücksetzung benötigen Sie für jeden der Prozesse keine eigene Pipe. Jede benannte Pipe hat einen Server und einen oder mehrere Clients. Wenn viele Clients vorhanden sind, werden vom jeweiligen Betriebssystem für jeden der Clients automatisch mehrere Instanzen der gleichnamigen Pipes erstellt. Alle Instanzen einer benannten Pipe teilen denselben Pipe-Namen, aber jede Instanz hat ihre eigenen Puffer und Handles und bietet einen separaten Conduit für die Client / Server-Kommunikation. Die Verwendung von Instanzen ermöglicht mehreren Pipe-Clients die gleichzeitige Verwendung derselben benannten Pipe. Jeder Prozess kann sowohl als Server für eine Pipe als auch als Client für eine andere Pipe fungieren und umgekehrt, wodurch eine Peer-to-Peer-Kommunikation möglich wird.

Wenn Sie eine Named Pipe verwenden, sind die Ereignisse in Ihrem Szenario überhaupt nicht erforderlich, und die Daten werden garantiert geliefert, unabhängig davon, was mit den Prozessen geschieht - jeder der Prozesse kann lange Verzögerungen haben (z. B. durch einen Tausch), aber die Daten werden schließlich ohne Ihre besondere Beteiligung so schnell wie möglich geliefert.

Ein Ereignis für alle Threads (Prozesse) ist nur OK, wenn die Benachrichtigung nur einmal erfolgt. In diesem Fall benötigen Sie das Ereignis "Manuelles Zurücksetzen", kein automatisches Zurücksetzen. Wenn Sie beispielsweise mitteilen möchten, dass Ihre Anwendung sehr bald beendet wird, können Sie dieses allgemeine Ereignis zum manuellen Zurücksetzen signalisieren. Aber, wie ich zuvor schrieb, in Ihrem Szenario ist Named Pipes die beste Wahl.

    
Maxim Masiutin 11.05.2017 03:49
quelle

Tags und Links