2013-02-25 16 views
6

Mam następujący kod, który ma na celu, aby czekać na wszystkich podanych uchwytami czekać, ale można rozwiązać przez konkretnego uchwytu oczekiwania:C# WaitHandle anulować 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; 
} 

To działa tylko na imprezy ManualReset. Podczas używania zdarzeń AutoReset WaitAny resetuje wszystkie sygnalizowane zdarzenia, ale zwraca tylko pierwszą sygnalizowaną (zgodnie z MSDN).

Jakieś pomysły, jak to zrobić przy pomocy zdarzeń AutoReset we właściwy sposób, bez odpytywania?

+0

Spróbuj użyć jednej z przeciążonych metod. I spróbuj utworzyć tablicę przed wprowadzeniem do-go, może zyskasz nowe spostrzeżenia. –

+0

Nie rozumiem, jak to zadziała, jeśli wydarzenie anulowania zostanie podniesione? – LukeHennerley

+1

Jeśli wydarzenie anulowania zostanie podniesione, oczekiwanie na wszystkie podane waitHandles zostanie anulowane – Harry13

Odpowiedz

1

Myślę, że twoja metoda powinna działać poprawnie na piśmie.

wierzę, że WaitHandle.WaitAny() używa the Windows API function WaitForMultipleObjects(), dokumentacja dla których mówi:

Modyfikacja występuje jedynie dla obiektu lub obiektów, których stan sygnalizowany spowodował, że funkcja zwraca.

Jeśli jest to prawda, oznacza to, że kod powinien działać.

Napisałem program testowy. Tworzy ładunek AutoResetEvents i ustawia połowę z nich przed wywołaniem CancelableWaitAll(). Następnie rozpoczyna wątek, który czeka 5 sekund przed ustawieniem drugiej połowy AutoResetEvents. Natychmiast po uruchomieniu tego wątku główny wątek wywołuje funkcję CancelableWaitAll().

Jeśli funkcja WaitAny() rzeczywiście zresetuje jakiekolwiek zdarzenia z autoresetem inne niż ten, którego indeks został zwrócony, funkcja CancelableWaitAll() nigdy nie powróci.

Ponieważ ma powrócić (po 5 sekundach oczywiście), ja twierdząc, że kod działa z AutoResetEvents:

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; 
     } 
    } 
} 

Niestety, nie mogę udowodnić, że WaitHandle.WaitAll() używa WaitForMultipleObjects (). Jeśli jednak nie, możesz wywołać to samodzielnie, używając funkcji WaitHandle.SafeWaitHandle, aby uzyskać obsługę zdarzeń systemu operacyjnego i użyć polecenia P/Invoke, aby wywołać funkcję WaitForMultipleObjects().