Po pierwsze, ślady stosu nie robią tego, co większość ludzi uważa. Mogą być przydatne podczas debugowania, ale nie są przeznaczone do użycia w środowisku wykonawczym, szczególnie w ASP.NET.
Również ślad stosu jest technicznie o gdzie kod wraca do nie gdzie kod pochodzi. Z prostym (synchronicznym) kodem, oba są takie same: kod zawsze powraca do jakiejkolwiek metody, która go nazywa. Jednak w przypadku kodu asynchronicznego te dwa są różne. Znowu ślad stosu mówi, co się stanie, następnie, ale interesuje Cię, co się stało w przeszłości.
Ramka stosu nie jest poprawną odpowiedzią na twoje potrzeby. Eric Lippert explains this well in his answer here.
The MSDN article że @ColeCampbell związanej opisuje jeden sposób, aby śledzić „łańcuchy casuality” (gdzie kod przyszedł z) z kodem async
. Niestety, takie podejście jest ograniczone (np. Nie obsługuje scenariuszy fork/join); jest to jednak jedyne znane mi podejście, które działa w aplikacjach Windows Store.
Od kiedy korzystasz z platformy ASP.NET z pełnym środowiskiem wykonawczym .NET 4.5, masz dostęp do wydajniejszego rozwiązania do śledzenia łańcuchów sukcesów: kontekstu logicznych połączeń. Twoje metody async
muszą jednak "włączyć", więc nie dostaniesz za darmo, jak za pomocą śledzenia stosu. Właśnie napisałem to w blogu, który nie został jeszcze opublikowany, więc dostajesz podgląd. :)
można zbudować "stos" połączeń samodzielnie wokół logicznym kontekście połączenia takie jak:
public static class MyStack
{
// (Part A) Provide strongly-typed access to the current stack
private static readonly string slotName = Guid.NewGuid().ToString("N");
private static ImmutableStack<string> CurrentStack
{
get
{
var ret = CallContext.LogicalGetData(name) as ImmutableStack<string>;
return ret ?? ImmutableStack.Create<string>();
}
set { CallContext.LogicalSetData(name, value); }
}
// (Part B) Provide an API appropriate for pushing and popping the stack
public static IDisposable Push([CallerMemberName] string context = "")
{
CurrentStack = CurrentStack.Push(context);
return new PopWhenDisposed();
}
private static void Pop() { CurrentContext = CurrentContext.Pop(); }
private sealed class PopWhenDisposed : IDisposable
{
private bool disposed;
public void Dispose()
{
if (disposed) return;
Pop();
disposed = true;
}
}
// (Part C) Provide an API to read the current stack.
public static string CurrentStackString
{
get { return string.Join(" ", CurrentStack.Reverse()); }
}
}
(ImmutableStack
jest dostępny here). Następnie można go używać tak:
static async Task SomeWork()
{
using (MyStack.Push())
{
...
Console.WriteLine(MyStack.CurrentStackAsString + ": Hi!");
}
}
Zaletą tego podejścia jest to, że działa z wszystkieasync
kod: fork/join, niestandardowe awaitables, ConfigureAwait(false)
, itd. Wadą jest to, że jesteś dodając trochę narzut. Również to podejście działa tylko na .NET 4.5; kontekst logicznego wywołania w .NET 4.0 nie jest zgodny z async
i nie będzie działał prawidłowo pod numerem .
Aktualizacja: Wydałem NuGet package (described on my blog), który wykorzystuje PostSharp do automatycznego wprowadzania popychnięć i wyskakuje. Tak więc uzyskanie dobrego śladu powinno być teraz o wiele prostsze.
Możesz zajrzeć do [tego artykułu] (http://msdn.microsoft.com/en-us/magazine/jj891052.aspx) na temat asynchronicznych łańcuchów przyczyn. –
@ChaseMedallion: Zobacz moją zaktualizowaną odpowiedź. –
Nie bezpośrednio odpowiedź, ale [ta odpowiedź] (http://stackoverflow.com/a/28633192/868227) daje wskazówkę, jak odszyfrować oryginalną metodę podaną ramkę stosu asynchronicznego. –