Wprowadzenie do debugowania aplikacji wielowątkowych (C#, Visual Basic, C++)

Program Visual Studio udostępnia kilka narzędzi i elementów interfejsu użytkownika, które ułatwiają debugowanie aplikacji wielowątkowych. W tym samouczku pokazano, jak używać znaczników wątków, okna stosów równoległych, okna obserwatora równoległego, warunkowych punktów przerwania i filtrowania punktów przerwania. Wykonanie czynności opisanych w tym samouczku zawiera informacje o funkcjach programu Visual Studio na potrzeby debugowania aplikacji wielowątków.

Te dwa artykuły zawierają dodatkowe informacje na temat korzystania z innych narzędzi debugowania wielowątkowego:

  • Aby użyć paska narzędzi Lokalizacja debugowania i okna Wątki , zobacz Przewodnik: Debugowanie aplikacji wielowątku.

  • Aby zapoznać się z przykładem korzystającym Task z (kodu zarządzanego) i środowiska uruchomieniowego współbieżności (C++), zobacz Przewodnik: debugowanie aplikacji równoległej. Ogólne porady dotyczące debugowania, które mają zastosowanie do większości typów aplikacji wielowątkowych, przeczytaj ten artykuł i ten.

Pierwszym krokiem jest utworzenie wielowątkowego projektu aplikacji.

Tworzenie wielowątkowego projektu aplikacji

  1. Otwórz program Visual Studio i utwórz nowy projekt.

    Jeśli okno uruchamiania nie jest otwarte, wybierz pozycję Okno uruchamiania pliku>.

    W oknie uruchamiania wybierz pozycję Utwórz nowy projekt.

    W oknie Tworzenie nowego projektu wprowadź lub wpisz konsolę w polu wyszukiwania. Następnie wybierz pozycję C#, C++lub Visual Basic z listy Język, a następnie wybierz pozycję Windows z listy Platforma.

    Po zastosowaniu filtrów języka i platformy wybierz szablon Aplikacja konsolowa dla platformy .NET lub C++, a następnie wybierz przycisk Dalej.

    Uwaga

    Jeśli nie widzisz poprawnego szablonu, przejdź do pozycji Narzędzia Pobierz narzędzia>i funkcje..., co spowoduje otwarcie Instalator programu Visual Studio. Wybierz pakiet roboczy Programowanie aplikacji klasycznych .NET lub Programowanie aplikacji klasycznych przy użyciu języka C++, a następnie wybierz pozycję Modyfikuj.

    W oknie Konfigurowanie nowego projektu wpisz lub wprowadź ciąg MyThreadWalkthroughApp w polu Nazwa projektu. Następnie wybierz pozycję Dalej lub Utwórz, niezależnie od tego, która opcja jest dostępna.

    W przypadku projektu .NET Core lub .NET 5+ wybierz zalecaną platformę docelową lub platformę .NET 8, a następnie wybierz pozycję Utwórz.

    Zostanie wyświetlony nowy projekt konsoli. Po utworzeniu projektu zostanie wyświetlony plik źródłowy. W zależności od wybranego języka plik źródłowy może mieć nazwę Program.cs, MyThreadWalkthroughApp.cpp lub Module1.vb.

  2. Usuń kod wyświetlany w pliku źródłowym i zastąp go następującym zaktualizowanym kodem. Wybierz odpowiedni fragment kodu dla konfiguracji kodu.

    using System;
    using System.Threading;
    
    public class ServerClass
    {
    
        static int count = 0;
        // The method that will be called when the thread is started.
        public void InstanceMethod()
        {
            Console.WriteLine(
                "ServerClass.InstanceMethod is running on another thread.");
    
            int data = count++;
            // Pause for a moment to provide a delay to make
            // threads more apparent.
            Thread.Sleep(3000);
            Console.WriteLine(
                "The instance method called by the worker thread has ended. " + data);
        }
    }
    
    public class Simple
    {
        public static void Main()
        {
            for (int i = 0; i < 10; i++)
            {
                CreateThreads();
            }
        }
        public static void CreateThreads()
        {
            ServerClass serverObject = new ServerClass();
    
            Thread InstanceCaller = new Thread(new ThreadStart(serverObject.InstanceMethod));
            // Start the thread.
            InstanceCaller.Start();
    
            Console.WriteLine("The Main() thread calls this after "
                + "starting the new InstanceCaller thread.");
    
        }
    }
    
  3. W menu File (Plik) wybierz polecenie Save all (Zapisz wszystko).

  4. (Tylko Visual Basic) W Eksplorator rozwiązań (okienko po prawej stronie) kliknij prawym przyciskiem myszy węzeł projektu, wybierz pozycję Właściwości. Na karcie Aplikacja zmień obiekt startowy na Prosty.

Debugowanie aplikacji wielowątkowej

  1. W edytorze kodu źródłowego poszukaj następującego fragmentu kodu:

    Thread.Sleep(3000);
    Console.WriteLine();
    
  2. Kliknij lewym przyciskiem myszy lewą rynną instrukcji Thread.Sleep lub dla języka C++, std::this_thread::sleep_for aby wstawić nowy punkt przerwania.

    W rynnie czerwony okrąg wskazuje, że punkt przerwania jest ustawiony w tej lokalizacji.

  3. W menu Debugowanie wybierz pozycję Rozpocznij debugowanie (F5).

    Program Visual Studio kompiluje rozwiązanie, aplikacja zaczyna działać z dołączonym debugerem, a następnie aplikacja zatrzymuje się w punkcie przerwania.

  4. W edytorze kodu źródłowego znajdź wiersz zawierający punkt przerwania.

Odnajdywanie znacznika wątku

  1. Na pasku narzędzi debugowania wybierz przycisk Show Threads in SourcePokaż wątki w źródle .

  2. Naciśnij klawisz F11 dwa razy, aby przejść do debugera.

  3. Spójrz na rynnę po lewej stronie okna. W tym wierszu zwróć uwagę na ikonę Thread Marker znacznika wątku przypominającą dwa skręcone wątki. Znacznik wątku wskazuje, że w tej lokalizacji zatrzymano wątek.

    Znacznik wątku może być częściowo ukryty przez punkt przerwania.

  4. Umieść wskaźnik na znaczniku wątku. Zostanie wyświetlona etykietka danych z informacją o nazwie i numerze identyfikatora wątku dla każdego zatrzymanego wątku. W tym przypadku nazwa to prawdopodobnie <noname>.

    Screenshot of the Thread ID in a DataTip.

  5. Wybierz znacznik wątku, aby wyświetlić dostępne opcje w menu skrótów.

Wyświetlanie lokalizacji wątków

W oknie Stosy równoległe można przełączać się między widokiem Wątki i (w przypadku programowania opartego na zadaniach) widoku Zadania, a także wyświetlić informacje o stosie wywołań dla każdego wątku. W tej aplikacji możemy użyć widoku Wątki.

  1. Otwórz okno Stosy równoległe, wybierając pozycję Debuguj>stosy równoległe systemu Windows.> Powinna zostać wyświetlona zawartość podobna do poniższej. Dokładne informacje mogą się różnić w zależności od bieżącej lokalizacji każdego wątku, sprzętu i języka programowania.

    Screenshot of the Parallel Stacks Window.

    W tym przykładzie od lewej do prawej są widoczne następujące informacje dotyczące kodu zarządzanego:

    • Bieżący wątek (żółta strzałka) został wprowadzony ServerClass.InstanceMethod. Identyfikator wątku i ramka stosu wątku można wyświetlić, umieszczając wskaźnik myszy na ServerClass.InstanceMethodobiekcie .
    • Wątek 31724 czeka na blokadę należącą do wątku 20272.
    • Wątek główny (po lewej stronie) został zatrzymany na stronie [Kod zewnętrzny], który można wyświetlić szczegółowo, jeśli wybierzesz pozycję Pokaż kod zewnętrzny.

    Parallel Stacks Window

    W tym przykładzie od lewej do prawej są widoczne następujące informacje dotyczące kodu zarządzanego:

    • Wątek główny (po lewej stronie) został zatrzymany na Thread.Start, gdzie punkt zatrzymania jest identyfikowany przez ikonę Thread Markerznacznika wątku .
    • Wprowadzono dwa wątki ServerClass.InstanceMethod, z których jeden jest bieżącym wątkiem (żółta strzałka), podczas gdy drugi wątek został zatrzymany w elemencie Thread.Sleep.
    • Nowy wątek (po prawej stronie) jest również uruchamiany, ale jest zatrzymany na .ThreadHelper.ThreadStart
  2. Aby wyświetlić wątki w widoku listy, wybierz pozycję Debuguj>wątki systemu Windows.>

    Screenshot of the Threads Window.

    W tym widoku można łatwo zobaczyć, że wątek 20272 jest wątkiem głównym i znajduje się obecnie w kodzie zewnętrznym, w szczególności System.Console.dll.

    Uwaga

    Aby uzyskać więcej informacji na temat korzystania z okna Wątki , zobacz Przewodnik: debugowanie aplikacji wielowątkowej.

  3. Kliknij prawym przyciskiem myszy wpisy w oknie Stosy równoległe lub Wątki, aby wyświetlić dostępne opcje w menu skrótów .

    Z tych menu po kliknięciu prawym przyciskiem myszy można wykonywać różne akcje. W tym samouczku zapoznasz się z bardziej szczegółowymi informacjami w oknie Monitorowanie równoległe (następne sekcje).

Ustawianie zegarka w zmiennej

  1. Otwórz okno Równoległy zegarek, wybierając pozycję Debuguj >równoległy zegarek z systemem Windows>Parallel Watch>1.

  2. Wybierz komórkę, w której zostanie wyświetlony <Add Watch> tekst (lub pusta komórka nagłówka w czwartej kolumnie), a następnie wprowadź ciąg data.

    Wartości zmiennej danych dla każdego wątku są wyświetlane w oknie.

  3. Wybierz komórkę, w której zostanie wyświetlony <Add Watch> tekst (lub pusta komórka nagłówka w piątej kolumnie), a następnie wprowadź wartość count.

    Wartości zmiennej count dla każdego wątku są wyświetlane w oknie. Jeśli nie widzisz jeszcze tych wielu informacji, spróbuj nacisnąć klawisz F11 kilka razy, aby przejść do wykonywania wątków w debugerze.

    Parallel Watch Window

  4. Kliknij prawym przyciskiem myszy jeden z wierszy w oknie, aby wyświetlić dostępne opcje.

Oflagowanie i usuwanie oflagowania wątków

Wątki można flagować, aby śledzić ważne wątki i ignorować inne wątki.

  1. W oknie Zegarek równoległy przytrzymaj naciśnięty klawisz Shift i wybierz wiele wierszy.

  2. Kliknij prawym przyciskiem myszy i wybierz pozycję Flaga.

    Wszystkie wybrane wątki są oflagowane. Teraz możesz filtrować, aby wyświetlić tylko oflagowane wątki.

  3. W oknie Monitorowanie równoległe wybierz przycisk Show Flagged ThreadsPokaż tylko oflagowane wątki.

    Na liście są wyświetlane tylko oflagowane wątki.

    Napiwek

    Po oflagowanych niektórych wątkach możesz kliknąć prawym przyciskiem myszy wiersz kodu w edytorze kodu i wybrać polecenie Uruchom oflagowane wątki do kursora. Upewnij się, że wybrano kod, z którym będą dostępne wszystkie oflagowane wątki. Program Visual Studio wstrzyma wątki w wybranym wierszu kodu, co ułatwia kontrolowanie kolejności wykonywania przez zamrażanie i odmrażanie wątków.

  4. Ponownie wybierz przycisk Pokaż tylko oflagowane wątki, aby powrócić do trybu Pokaż wszystkie wątki.

  5. Aby cofnąć opóźnienie wątków, kliknij prawym przyciskiem myszy co najmniej jeden oflagowany wątek w oknie Monitor równoległy i wybierz polecenie Coflaguj.

Blokowanie i rozmrażanie wykonania wątku

Napiwek

Możesz zablokować i rozmrozić (wstrzymać i wznowić) wątki, aby kontrolować kolejność, w której wątki wykonują pracę. Może to pomóc w rozwiązaniu problemów ze współbieżnością, takich jak zakleszczenia i warunki wyścigu.

  1. W oknie Równoległy zegarek ze wszystkimi wybranymi wierszami kliknij prawym przyciskiem myszy i wybierz pozycję Zablokuj.

    W drugiej kolumnie zostanie wyświetlona ikona wstrzymania dla każdego wiersza. Ikona wstrzymania wskazuje, że wątek jest zamrożony.

  2. Usuń zaznaczenie wszystkich innych wierszy, wybierając tylko jeden wiersz.

  3. Kliknij prawym przyciskiem myszy wiersz i wybierz polecenie Thaw.

    Ikona wstrzymania odchodzi w tym wierszu, co oznacza, że wątek nie jest już zamrożony.

  4. Przejdź do edytora kodu i naciśnij klawisz F11. Tylko niezamrożony wątek jest uruchamiany.

    Aplikacja może również utworzyć wystąpienie nowych wątków. Wszystkie nowe wątki są niefłoszone i nie są zamrożone.

Obserwowanie pojedynczego wątku z warunkowymi punktami przerwania

Pomocne może być wykonanie pojedynczego wątku w debugerze. Jednym ze sposobów, aby to zrobić, jest zamrażanie wątków, których nie interesujesz. W niektórych scenariuszach może być konieczne wykonanie pojedynczego wątku bez zamrażania innych wątków, na przykład w celu odtworzenia konkretnej usterki. Aby postępować zgodnie z wątkiem bez zamrażania innych wątków, należy unikać włamywania się do kodu z wyjątkiem wątku, który cię interesuje. To zadanie można wykonać, ustawiając warunkowy punkt przerwania.

Punkty przerwania można ustawić w różnych warunkach, takich jak nazwa wątku lub identyfikator wątku. Pomocne może być ustawienie warunku danych, które znasz, jest unikatowe dla każdego wątku. Takie podejście jest typowe podczas debugowania, gdy interesuje Cię określona wartość danych niż w jakimkolwiek konkretnym wątku.

  1. Kliknij prawym przyciskiem myszy utworzony wcześniej punkt przerwania i wybierz pozycję Warunki.

  2. W oknie punkt przerwania Ustawienia wprowadź data == 5 wartość dla wyrażenia warunkowego.

    Conditional Breakpoint

    Napiwek

    Jeśli interesuje Cię konkretny wątek, użyj nazwy wątku lub identyfikatora wątku dla warunku. Aby to zrobić w oknie punkt przerwania Ustawienia, wybierz pozycję Filtruj zamiast wyrażenia warunkowego i postępuj zgodnie z poradami dotyczącymi filtru. Możesz nazwać wątki w kodzie aplikacji, ponieważ identyfikatory wątków zmieniają się po ponownym uruchomieniu debugera.

  3. Zamknij okno Ustawienia punktu przerwania.

  4. Wybierz przycisk Uruchom ponownie Restart App , aby ponownie uruchomić sesję debugowania.

    Należy podzielić kod w wątku, w którym wartość zmiennej danych wynosi 5. W oknie Monitorowanie równoległe wyszukaj żółtą strzałkę wskazującą bieżący kontekst debugera.

  5. Teraz możesz przejść przez kod (F10) i przejść do kodu (F11) i postępować zgodnie z wykonywaniem pojedynczego wątku.

    Tak długo, jak warunek punktu przerwania jest unikatowy dla wątku, a debuger nie osiąga żadnych innych punktów przerwania w innych wątkach (może być konieczne ich wyłączenie), możesz przejść przez kod i przejść do kodu bez przełączania się do innych wątków.

    Uwaga

    Po postępie debugera wszystkie wątki zostaną uruchomione. Jednak debuger nie podzieli kodu na inne wątki, chyba że jeden z innych wątków trafi do punktu przerwania.