2013-03-01 8 views
12

Mam obiekt polecenia, wykonujący pracę na podstawie żądania z kolejki żądań. To konkretne polecenie wykona swoją pracę w domenie podrzędnej. Część wykonywania jego pracy w domenie podrzędnej obejmuje blokowanie operacji ConcurrentQueue (np. Dodaj lub weź). Muszę być w stanie propagować przerwany sygnał w kolejce zgłoszeń, w poprzek do aplikacji podrzędnej podrzędnej i aby w nim obudzić wątki robocze.Jak przekazać CancellationToken przez granicę AppDomain?

Dlatego uważam, że muszę przekazać Token Anulowania przez granicę AppDomain.

Próbowałem tworzenia klasy, która dziedziczy z MarshalByRefObject:

protected class InterAppDomainAbort : MarshalByRefObject, IAbortControl 
    { 
     public InterAppDomainAbort(CancellationToken t) 
     { 
      Token = t; 
     } 

     [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.Infrastructure)] 
     public override object InitializeLifetimeService() 
     { 
      return null; 
     } 

     public CancellationToken Token 
     { 
      get; 
      private set; 
     } 

    }; 

i przechodzącej to jako argument na funkcję pracownika:

// cts is an instance variable which can be triggered by another thread in parent appdomain 
cts = new CancellationTokenSource(); 
InterAppDomainAbort abortFlag = new InterAppDomainAbort(cts.Token); 
objectInRemoteAppDomain = childDomain.CreateInstanceAndUnwrap(...); 

// this call will block for a long while the work is being performed. 
objectInRemoteAppDomain.DoWork(abortFlag); 

Ale wciąż otrzymuję wyjątek, gdy objectInRemoteAppDomain próbuje dostęp do właściwości Token getter:

System.Runtime.Serialization.SerializationException: Type 'System.Threading.CancellationToken' in Assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' is not marked as serializable. 

Moje pytanie to: Jak mogę propagować sygnał przerwania/anulowania w aplikacjach i budzić wątki, które mogą być blokowane w strukturach danych współbieżności platformy .NET (gdzie obsługiwane są argumenty AnulowanieToken).

Odpowiedz

19

Minęło trochę czasu, odkąd spojrzałem na różne rzeczy związane z AppDomain, więc mogą być problemy z tym kodem, których nie zdawałem sobie sprawy, ale wydaje się, że wykonuję to zadanie. Podstawowy problem polega na tym, że wydaje się, że nie można przenieść sygnału anulowania [źródła] z jednego elementu AppDomain na inny. Tak więc tworzę dwa źródła, z podstawową konfiguracją do anulowania wtórnego, gdy jest to właściwe.

Fakt, że jest dwoma oddzielnymi źródłami tokenów w tym scenariuszu, może oczywiście stanowić problem, ale nie sądzę, że pozbędziesz się faktu, że brak możliwości szeregowania uniemożliwia użycie tego samego w w każdym razie dwie oddzielne domeny aplikacji.

standardowe ostrzeżenia o minimalnym Sprawdzanie błędów Dispose wdrożeń itp

// I split this into a separate interface simply to make the boundary between 
// canceller and cancellee explicit, similar to CancellationTokenSource itself. 
public interface ITokenSource 
{ 
    CancellationToken Token { get; } 
} 

public class InterAppDomainCancellable: MarshalByRefObject, 
             ITokenSource, 
             IDisposable 
{ 
    public InterAppDomainCancellable() 
    { 
     cts = new CancellationTokenSource(); 
    } 

    public void Cancel() { cts.Cancel(); } 

    // Explicitly implemented to make it less tempting to call Token 
    // from the wrong side of the boundary. 
    CancellationToken ITokenSource.Token { get { return cts.Token; } } 

    public void Dispose() { cts.Dispose(); } 

    private readonly CancellationTokenSource cts; 
} 

// ... 

// Crucial difference here is that the remotable cancellation source 
// also lives in the other domain. 
interAppDomainCancellable = childDomain.CreateInstanceAndUnwrap(...); 

var primaryCts = new CancellationTokenSource(); 
// Cancel the secondary when the primary is cancelled. 
primaryCts.Token.Register(() => interAppDomainCancellable.Cancel()); 

objectInRemoteAppDomain = childDomain.CreateInstanceAndUnwrap(...); 
// DoWork expects an instance of ITokenSource. 
// It can access Token because they're all in the same domain together. 
objectInRemoteAppDomain.DoWork(interAppDomainCancellable); 
+0

To najlepsze rozwiązanie, o ile mogę to stwierdzić. – usr

0

jest rzeczywiście znacznie łatwiejszy sposób, aby pokonać tę przeszkodę, zakładając swój typ serwera proxy jest pojedynczym odpowiedzialność. Zakładam oczywiście, że zachowujesz kolekcję swoich utworzonych domen i rozładowujesz je, gdy twoja aplikacja zostanie zamknięta lub Twój obiekt zostanie usunięty. Zakładam również, że powodem, dla którego potrzebujesz tokenu anulowania, jest anulowanie operacji asynchronicznej w swoim typie referencyjnym. Po prostu wykonaj następujące czynności:

Utwórz pola tokenSource i tokenów i zainicjuj je w swoim konstruktorze.

_cancellationTokenSource = new CancellationTokenSource(); 
_token = _cancellationTokenSource.Token; 

Subskrybuj następujące zdarzenia. Wyjątek UnhandledException służy do wychwycenia dowolnego wyjątku powodującego błędy, który powoduje przedwczesne zakończenie Twojej domeny. To powinna być najlepsza praktyka.

var currDomain = AppDomain.CurrentDomain; 
      currDomain.DomainUnload += currDomain_DomainUnload; 
      currDomain.UnhandledException += currDomain_UnhandledException; 

Anuluj połączenie w źródle tokenu po wywołaniu zdarzenia zwalniania domeny. Dodatkowo możesz chcieć dysponować metodą dispose, która anuluje subskrypcję zdarzeń domeny, która zostanie wywołana z lub po prostu niech proces czyszczenia wątków domeny zostanie usunięty.

void currDomain_DomainUnload(object sender, EventArgs e) 
    { 
     _log.Debug(FormatLogMessage(_identity, "Domain unloading Event!")); 
     _cancellationTokenSource.Cancel(); 
     _logPlayer.Dispose(); 
    } 

void currDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) 
    { 
     _log.Error(string.Format("***APP Domain UHE*** Error:{0}", e.ExceptionObject); 
     _cancellationTokenSource.Cancel(); 
     _logPlayer.Dispose(); 
    } 
+0

Sprytny. Bardzo dobrze. – gap