2014-06-28 42 views
8

Próbuję znaleźć sposób na śledzenie przepływu wykonywania zadań asynchronicznych w sposób, który byłby łatwy do zrozumienia w odniesieniu do zadania, jaki był pierwotny przepływ, który go zainicjował.Śledzenie przepływu zadań C#/.NET

Potrzebuję go głównie do rejestrowania, debugowania i zachowywania sortowania stosu dla określonego przepływu wykonywania.

na przykład: jeśli mam serwer z wieloma klientami z wielu adresów IP i serwer musi wykonać przepływ pracy dla każdego klienta, który obejmuje wiele akcji asynchronicznych, a tym samym obejmuje wiele różnych zadań na przepływ wykonania; rejestrowanie takiego przepływu jest trudne, szczególnie w przypadku korzystania z mechanizmu asynchronicznego/oczekującego.

Nie znalazłem sposobu na owijanie zadań w taki sposób, aby dla każdego wykonywanego zadania znałem początkowy opis przepływu podczas rejestrowania, na przykład, gdy uruchamiam nowy przepływ zadań dla akcji z opis - "Dbanie o klienta 10.0.3.4" chcę móc dodać ten opis do każdego elementu dziennika wychodzącego z tego przepływu w dowolnym zadaniu, które z niego utworzono.

Podczas korzystania z wątków tylko jego łatwe, ponieważ masz zmienne statyczne wątku. z zadaniami jest niemożliwe ... Próbowałem nawet utworzyć własny program do planowania zadań, który będzie owijał każde zadanie, które go używa (nawet gdy używa się metod async/await), ale dostał się w ślepy zaułek, ponieważ baza harmonogramu zadań czasami używa nowego wątku (nawet jeśli nie było żadnej niejawnej prośby o użycie nowego wątku) - metoda "TryExecuteTaskInline" może czasami działać w nowym wątku.

Każdy pomysł lub sugestia, w jaki sposób mogę to osiągnąć?

+1

Co powiesz na pokazanie nam kodu z tym, co próbujesz zrobić? –

Odpowiedz

11

Można użyć Trace.CorrelationManager.ActivityId do przechowywania logicznego identyfikatora operacji lub jeszcze lepszego przechowywania ImmutableStack logicznych identyfikatorów operacji. Jest on przechowywany w CallContext i jest kopiowany przez metody async wzywa:

public static class LogicalFlow 
{ 
    private static readonly string _name = typeof (LogicalFlow).Name; 

    private static ImmutableStack<Guid> LogicalStack 
    { 
     get 
     { 
      return CallContext.LogicalGetData(_name) as ImmutableStack<Guid> ?? ImmutableStack.Create<Guid>(); 
     } 
     set 
     { 
      CallContext.LogicalSetData(_name, value); 
     } 
    } 

    public static Guid CurrentId 
    { 
     get 
     { 
      var logicalStack = LogicalStack; 
      return logicalStack.IsEmpty ? Guid.Empty : logicalStack.Peek(); 
     } 
    } 
} 

Można go używać jako IDisposable dzięki czemu można wykorzystywać zakres using aby upewnić się, że to Pop aby każdy Push:

private static readonly Popper _popper = new Popper(); 

public static IDisposable StartScope() 
{ 
    LogicalStack = LogicalStack.Push(Guid.NewGuid()); 
    return _popper; 
} 

private sealed class Popper : IDisposable 
{ 
    public void Dispose() 
    { 
     LogicalStack = LogicalStack.Pop(); 
    } 
} 

Zastosowanie:

using (LogicalFlow.StartScope()) 
{ 
    Console.WriteLine(LogicalFlow.CurrentId); 
    await DoSomethingAsync(); 
    Console.WriteLine(LogicalFlow.CurrentId); 
} 

Ta odpowiedź uprzednio powołanych Trace.CorrelationManager.LogicalOperationStack ale Is LogicalOperationStack incompatible with async in .Net 4.5

+0

Dzięki temu szukam rozwiązania :) Wspomnę tylko, że możesz wcisnąć do tego stosu inne obiekty, a nie tylko Guidy. – ThaPhoeniX

+1

To prawda ... Ale upewnij się, że naciskasz tylko na typy niezmienne. – i3arnon

+0

@ i3arnon: Rozumiem, co masz na myśli. Teraz rozumiem cały problem. – drowa

2

z zadaniami, można przechowywać tego rodzaju informacji w execution context. Here's an example jak to zrobić, używając kontekstu połączenia logicznego, który jest jednym rodzajem kontekstu wykonania. Trace.CorrelationManager jest również zbudowany na podstawie kontekstu wykonania.