8

Chcę użyć blokady lub podobnej synchronizacji do ochrony sekcji krytycznej. W tym samym czasie chcę posłuchać Tokenu Anulowania.C# blokuj i słuchaj AnulujToken

W tej chwili używam muteksu w ten sposób, ale mutex nie ma tak dobrej wydajności. Czy mogę używać innych klas synchronizacji (w tym nowego .Net 4.0) zamiast muteksu?

WaitHandle.WaitAny(new[] { CancelToken.WaitHandle, _mutex}); 
CancelToken.ThrowIfCancellationRequested(); 
+0

można zakładać jakiś kod pokazujący sekcji krytycznej i jak aktualnie uwalniający mutex? –

Odpowiedz

11

Spójrz na nowym .NET 4.0 Framework funkcji SemaphoreSlim Class. Zapewnia metodę SemaphoreSlim.Wait(CancellationToken).

Blokuje bieżącego wątku, dopóki można go wprowadzić SemaphoreSlim, natomiast obserwowania CancellationToken

Od pewnego punktu widzenia, używając Semaphore w takim prostym przypadku może być napowietrznych ponieważ początkowo został zaprojektowany, aby zapewnić dostęp do wielu wątków, ale być może uznasz to za przydatne.

EDIT: Fragment kodu

CancellationToken token = new CancellationToken();    
SemaphoreSlim semaphore = new SemaphoreSlim(1,1); 

try { 
    // block section entrance for other threads 
    semaphore.Wait(token); 

    // critical section code 
    // ... 
    if (token.IsCancellationRequested) 
    { 
     // ... 
    } 
} 
finally { 
    semaphore.Release(); 
} 
+0

Proszę, wypróbujcie w końcu :-) – xanatos

+0

@xanatos: masz na myśli jakiś wyjątek? To jest pseudo kod, który pokazuje jak SemaphoreSlim związał się z CancellationTokenem i nie więcej :) – sll

+0

@xanatos ... czemu nie zrobić tego sam? – Carsten

-2

można użyć Monitor obiekt zyskać trochę w wydajności jak stwierdzono także w MSDN:

Chociaż mutex może być stosowany do wewnątrz proces synchronizacji wątku, przy użyciu monitora jest generalnie korzystne, ponieważ monitory zostały zaprojektowane specjalnie dla platformy .NET Framework, a tym samym lepsze wykorzystanie zasobów

aby uzyskać więcej informacji

http://msdn.microsoft.com/en-us/library/system.threading.monitor.aspx

Model CancellationToken oferuje model do kooperatywnego anulowania asynchronicznych lub długotrwałych operacji synchronicznych. jeśli chcesz go użyć z klasą monitora, musisz ustrukturyzować kod, aby zwolnić blokadę, jeśli jest to żądanie anulowania. Można zrobić coś poniżej:

public void ThreadSafeMethod() 
     { 
      var cancellationToken=new CancellationToken(); 
      object locker=new object(); 
      Monitor.Enter(locker); 
      try 
      { 
       //your code 



       if (token.IsCancellationRequested) 
       { 
        Monitor.Exit(locker); 

       } 

      } 
      finally 
      { 
       Monitor.Exit(locker); 
      } 
     } 

lub jeśli chcesz używać ThrowIfCancellationRequested:

public void ThreadSafeMethod() 
     { 
      var cancellationToken=new CancellationToken(); 
      object locker=new object(); 
      Monitor.Enter(locker); 
      try 
      { 
       //your code 

       cancellationToken.ThrowIfCancellationRequested(); 
      } 
      catch(OperationCanceledException) 
      { 

      } 
      finally 
      { 
       Monitor.Exit(locker); 
      } 
     } 
+0

OK, ale to właśnie próbowałem zrobić. Czy możesz pokazać mi przykład? – Karsten

+0

Czy 'Monitor's można połączyć z' CancellationToken'? Nie wiedziałem ... – xanatos

+0

odpowiedź została zmieniona –

0
private object _lockObject = new object(); 

lock (_lockObject) 
{ 
    // critical section 
    using (token.Register(() => token.ThrowIfCancellationRequested()) 
    { 
     // Do something that might need cancelling. 
    } 
} 

Wywołanie Cancel() na tokena spowoduje ThrowIfCancellationRequested() powołano jako że było to, co jest podłączone do Register zwrotnego. Tutaj możesz umieścić dowolną logikę anulowania. Takie podejście jest wspaniałe, ponieważ można anulować blokowanie połączeń, wymuszając warunki, które spowodują zakończenie połączenia.

ThrowIfCancellationRequested rzuca wyjątek OperationCanceledException. Musisz sobie z tym poradzić w wątku wywołującym lub cały proces może zostać skrócony. Prostym sposobem na zrobienie tego jest uruchomienie zadania za pomocą klasy Zadanie, która będzie agregować wszystkie wyjątki do obsługi wątku wywołującego.

try 
{ 
    var t = new Task(() => LongRunningMethod()); 
    t.Start(); 
    t.Wait(); 
} 
catch (AggregateException ex) 
{ 
    ex.Handle(x => true); // this effectively swallows any exceptions 
} 

Niektóre dobre rzeczy here obejmujące spółdzielnia anulowanie

+1

To zgłasza wyjątek w wywołaniu wątku Anuluj – Karsten

+0

Jeśli masz na myśli wyjątek OperationCanceledException, to jest to zgodne z projektem - to jest to, co rzuca metoda ThrowIfCancellationRequested. Powinieneś obsłużyć go na dzwoniącym za pomocą zalecanego wzorca (zaktualizowana odpowiedź). –

+0

Odnośnie wyciągu "Wywołanie" Anuluj() 'na t̶o̶k̶e̶n̶ AnulowanieTokenSource spowoduje wywołanie' ThrowIfCancellationRequested() '" to zachowanie można kontrolować za pomocą [Anuluj (bool)] (https: // msdn .microsoft.com/en-us/library/dd321703 (v = vs.110) .aspx) przeciążenie. –