Możliwe jest pobranie stacktrace za pomocą System.Diagnostics.StackTrace, ale wątek musi zostać zawieszony. Funkcje zawieszenia i wznowienia są przestarzałe, więc spodziewam się, że istnieje lepszy sposób.Jak zdobyć stos przejściowy wątku?
Odpowiedz
Według C# 3.0 in a Nutshell, jest to jedna z nielicznych sytuacji, kiedy jest dobrze, aby zadzwonić do wstrzymania/wznowienia.
Myślę, że jeśli chcesz to zrobić bez współpracy z docelowym wątkiem (np. Poprzez wywołanie metody blokującej go na semaforze lub czymś, gdy twój wątek wykonuje ślad stosu), musisz użyć przestarzałe interfejsy API.
Ewentualną alternatywą jest użycie interfejsu COM-based ICorDebug używanego przez debugery .NET. MDbg codebase może dać początek:
Nie, COM nie jest rozwiązaniem. Suspend/Resume wydaje się bardziej czysty niż produkty COM z .NET ... – bh213
To jest stara nitka, ale chciałem tylko ostrzec o proponowanym rozwiązaniu: rozwiązanie Suspend and Resume nie działa - po prostu doświadczyłem zakleszczenia w moim kodzie próbując sekwencji Suspend/StackTrace/Resume.
Problem polega na tym, że konstruktor StackTrace wykonuje RuntimeMethodHandle -> konwersji MethodBase, a to zmienia wewnętrzną metodę MethodInfoCache, która przyjmuje blokadę. Zakleszczenie nastąpiło, ponieważ wątek, który badałem również robił odbicie i trzymał ten zamek.
Szkoda, że zawieszanie/wznawianie pracy nie jest wykonywane wewnątrz konstruktora StackTrace - później ten problem mógł zostać ominięty.
Zupełnie prawdziwa - spotkałem się z impasami. Wydaje się jednak, że istnieje obejście (zobacz moją odpowiedź). –
Oto, co pracował dla mnie do tej pory:
StackTrace GetStackTrace (Thread targetThread)
{
StackTrace stackTrace = null;
var ready = new ManualResetEventSlim();
new Thread (() =>
{
// Backstop to release thread in case of deadlock:
ready.Set();
Thread.Sleep (200);
try { targetThread.Resume(); } catch { }
}).Start();
ready.Wait();
targetThread.Suspend();
try { stackTrace = new StackTrace (targetThread, true); }
catch { /* Deadlock */ }
finally
{
try { targetThread.Resume(); }
catch { stackTrace = null; /* Deadlock */ }
}
return stackTrace;
}
Jeśli zakleszczenia, impas zostanie automatycznie zwolniona i wrócisz null śladu. (Możesz potem zadzwonić jeszcze raz.)
Powinienem dodać, że po kilku dniach testowania, tylko raz udało mi się stworzyć zakleszczenie na mojej maszynie Core i7. Zakleszczenia są powszechne w przypadku jednordzeniowej maszyny wirtualnej, gdy procesor działa na 100%.
Możesz chcieć użyć drugiego 'ManualResetEvent', aby uniknąć targetThread.Wznów() jest wykonywany i wyrzucanie wyjątku * za każdym razem * ... , jeśli (! NoDeadLockSafeGuard.WaitOne (200)) { try {celThread.Resume(); } catch {} } –
Istnieje jeszcze niewielkie ryzyko wystąpienia impasu: Jeśli środowisko wykonawcze zdecyduje się zawiesić główny wątek pomiędzy "ready.Wait()" i "targetThread.Suspend()", nadal możesz mieć impas od czasu upadku - wątek już wyszedł. IMO musisz mieć pętlę w wątku odblokowującym, która pozostaje tylko wtedy, gdy główny wątek sygnalizuje, że bezpiecznie opuścił funkcję. – Andreas
Thread.Suspend() i Thread.Resume() są oznaczone jako przestarzałe w Założeniach koncepcyjnych, więc ktoś przy użyciu ostrzeżenia jako błędy trzeba będzie użyć '#pragma warning disable 0618'' przed metody i ' '#pragma warning restore 0618'' następnie, aby skompilować ten kod. –
Jak wspomniano w moim komentarzu, proponowane rozwiązanie nadal ma niewielkie prawdopodobieństwo wystąpienia impasu. Poniżej znajdziesz moją wersję.
private static StackTrace GetStackTrace(Thread targetThread) {
using (ManualResetEvent fallbackThreadReady = new ManualResetEvent(false), exitedSafely = new ManualResetEvent(false)) {
Thread fallbackThread = new Thread(delegate() {
fallbackThreadReady.Set();
while (!exitedSafely.WaitOne(200)) {
try {
targetThread.Resume();
} catch (Exception) {/*Whatever happens, do never stop to resume the target-thread regularly until the main-thread has exited safely.*/}
}
});
fallbackThread.Name = "GetStackFallbackThread";
try {
fallbackThread.Start();
fallbackThreadReady.WaitOne();
//From here, you have about 200ms to get the stack-trace.
targetThread.Suspend();
StackTrace trace = null;
try {
trace = new StackTrace(targetThread, true);
} catch (ThreadStateException) {
//failed to get stack trace, since the fallback-thread resumed the thread
//possible reasons:
//1.) This thread was just too slow (not very likely)
//2.) The deadlock ocurred and the fallbackThread rescued the situation.
//In both cases just return null.
}
try {
targetThread.Resume();
} catch (ThreadStateException) {/*Thread is running again already*/}
return trace;
} finally {
//Just signal the backup-thread to stop.
exitedSafely.Set();
//Join the thread to avoid disposing "exited safely" too early. And also make sure that no leftover threads are cluttering iis by accident.
fallbackThread.Join();
}
}
}
jak sądzę, ManualResetEventSlim „fallbackThreadReady” nie jest naprawdę konieczne, ale dlaczego ryzykować niczego w tej delikatnej sprawie?
UWAGA: ManualResetEventSlim jest IDisposable –
@MarkSowul: Dodano instrukcję użycia . Dziękuję za podpowiedź. – Andreas
Czy można powiedzieć, że to podejście jest odporne na zakleszczenia? Edycja: Wspomniałeś o komentarzu, ale nie byłem pewien, czy odnosiłeś się do komentarza do powyższego rozwiązania dostarczonego przez PO. – Hatchling
Wygląda na to, że był to obsługiwane operacja w przeszłości, ale niestety, Microsoft udostępnił ten przestarzały: https://msdn.microsoft.com/en-us/library/t2k35tat(v=vs.110).aspx
- 1. Nieoczekiwany "przejściowy" modyfikator konstruktora
- 2. CoreData przejściowy związek przykładem
- 3. GCC - Jak wyrównać stos?
- 4. Jak zdobyć zdarzenia wątku wxpython w teście jednostkowym?
- 5. Uzyskaj stos wywołań z dowolnego wątku w C
- 6. Czy jest miejsce na stos dla każdego wątku?
- 7. Adapter przejściowy dla iteratora wskaźników
- 8. Jak uaktualnić stos ghc
- 9. Shared element przejściowy za pomocą ReactJS
- 10. Android Shared element przejściowy, źle pozycja początkowa
- 11. Jak zdobyć obiekty po połączeniu CoreData Context
- 12. Niezbilansowany stos!
- 13. Skrót przejściowy CSS z wieloma właściwościami?
- 14. Jak iterować stos w Javie
- 15. Jak mogę przepełnić mój stos?
- 16. Jak wydrukować stos stosu StackOverflowException
- 17. Jak zapisać obiekt przejściowy, który już istnieje w sesji NHibernate?
- 18. stos działań
- 19. Jak zdobyć czas przyjaciela?
- 20. jak zdobyć listę procesów
- 21. Jak zdobyć ulubione gwiazdy
- 22. Android - jak zdobyć Android.R.anim.slide_in_right
- 23. Jak zdobyć wszystkie kobiety?
- 24. Jak zdobyć AssemblyTitle?
- 25. Jak zdobyć zasoby graficzne
- 26. jak zdobyć token urządzenia?
- 27. Jak zdobyć wysokość LinearLayout
- 28. Jak zdobyć zainteresowania użytkowników?
- 29. Java Stos Porównanie
- 30. Jak uniknąć głodzenia głównego wątku?
Tak właśnie zrobiłem. – bh213
Uważaj, aby nie wprowadzić ostrych zakleszczeń. Jeśli zawiesisz wątek podczas trzymania blokady, będziesz potrzebować impasu. Najczęstszą przyczyną może być sytuacja, w której wątki współużytkują strumień (np. Zapis na konsoli lub podobne). –