2013-02-25 4 views
6

Ich habe den folgenden Code, der das Ziel für alle gegebenen Warte Griffe warten muss, ist aber durch eine bestimmte Warte Griff kündbaren:C# Waithandle kündbaren WaitAll

public static bool CancelableWaitAll(WaitHandle[] waitHandles, WaitHandle cancelWaitHandle) 
{ 
    var waitHandleList = new List<WaitHandle>(); 
    waitHandleList.Add(cancelWaitHandle); 
    waitHandleList.AddRange(waitHandles); 
    int handleIdx; 
    do 
    { 
     handleIdx = WaitHandle.WaitAny(waitHandleList.ToArray()); 
     waitHandleList.RemoveAt(handleIdx); 
    } 
    while (waitHandleList.Count > 1 && handleIdx != 0); 
    return handleIdx != 0; 
} 

Diese nur für ManualReset Ereignisse funktioniert. Bei der Verwendung von AutoReset-Ereignissen setzt WaitAny alle signalisierten Ereignisse zurück, gibt jedoch nur das erste signalisierte Signal (entsprechend MSDN) zurück.

Irgendwelche Ideen, wie man dies mit AutoReset-Ereignissen auf angemessene Weise ohne Abfragen erreicht?

+0

Versuchen Sie, eine der überladenen Methoden zu verwenden. Und versuchen Sie, das Array zu erstellen, bevor Sie do-while betreten, vielleicht erhalten Sie neue Einsichten. –

+0

Ich kann nicht verstehen, wie das funktioniert, wenn das Ereignis cancel ausgelöst wird? – LukeHennerley

+1

Wenn das Abbruchereignis ausgelöst wird, wird das Warten auf alle gegebenen waitHandles abgebrochen – Harry13

Antwort

1

Ich denke, dass Ihre Methode korrekt wie geschrieben funktionieren sollte.

Ich glaube, dass WaitHandle.WaitAny() verwendet the Windows API function WaitForMultipleObjects(), die Dokumentation, für die sagt:

Änderung nur für das Objekt oder Objekte, die auftritt signalisierten Zustand verursachte die Funktion zurückzukehren.

Wenn wahr, bedeutet dies, dass Ihr Code funktionieren sollte.

Ich schrieb ein Testprogramm. Es erstellt eine Ladung von AutoResetEvents und legt die Hälfte davon fest, bevor CancelableWaitAll() aufgerufen wird. Dann wird ein Thread gestartet, der 5 Sekunden wartet, bevor die andere Hälfte der AutoResetEvents gesetzt wird. Unmittelbar nach dem Start dieses Threads ruft der Hauptthread CancelableWaitAll() auf.

Wenn WaitAny() tatsächlich eines der Autoreset-Ereignisse außer demjenigen zurücksetzt, dessen Index zurückgegeben wurde, wird CancelableWaitAll() niemals zurückgegeben.

Weil es zurückgeht (nach 5 Sekunden natürlich), ich behaupten, dass Ihr Code mit AutoResetEvents funktioniert:

using System; 
using System.Collections.Generic; 
using System.Threading; 
using System.Threading.Tasks; 

namespace Demo 
{ 
    public static class Program 
    { 
     private static void Main(string[] args) 
     { 
      AutoResetEvent[] events = new AutoResetEvent[32]; 

      for (int i = 0; i < events.Length; ++i) 
      { 
       events[i] = new AutoResetEvent(false); 
      } 

      // Set the first 16 auto reset events before calling CancelableWaitAll(). 

      for (int i = 0; i < 16; ++i) 
      { 
       events[i].Set(); 
      } 

      // Start a thread that waits five seconds and then sets the rest of the events. 

      Task.Factory.StartNew(() => setEvents(events)); 

      Console.WriteLine("Waiting for all events to be set."); 

      ManualResetEvent stopper = new ManualResetEvent(false); 
      CancelableWaitAll(events, stopper); 

      Console.WriteLine("Waited."); 
     } 

     private static void setEvents(AutoResetEvent[] events) 
     { 
      Thread.Sleep(5000); 

      for (int i = 16; i < events.Length; ++i) 
      { 
       events[i].Set(); 
      } 
     } 

     public static bool CancelableWaitAll(WaitHandle[] waitHandles, WaitHandle cancelWaitHandle) 
     { 
      var waitHandleList = new List<WaitHandle>(); 
      waitHandleList.Add(cancelWaitHandle); 
      waitHandleList.AddRange(waitHandles); 
      int handleIdx; 
      do 
      { 
       handleIdx = WaitHandle.WaitAny(waitHandleList.ToArray()); 
       waitHandleList.RemoveAt(handleIdx); 
      } 
      while (waitHandleList.Count > 1 && handleIdx != 0); 
      return handleIdx != 0; 
     } 
    } 
} 

Leider kann ich nicht beweisen, dass WaitHandle.WaitAll() verwendet WaitForMultipleObjects (). Wenn dies jedoch nicht der Fall ist, können Sie es selbst aufrufen, indem Sie WaitHandle.SafeWaitHandle verwenden, um die Ereignishandles des Betriebssystems abzurufen und P/Invoke zum Aufrufen von WaitForMultipleObjects() zu verwenden.