25

Одна вещь, которая меня раздражает при отладке программ в Visual Studio (в моем случае в 2005 году), заключается в том, что когда я использую «step over» (путем нажатия) для выполнения следующая строка кода, я часто заканчиваю тем, что достигал этой конкретной строки кода в совершенно другом потоке, чем тот, на который я смотрел. Это означает, что весь контекст того, что я делал, был потерян.«Шаг за шагом» при отладке многопоточных программ в Visual Studio

Как мне обойти это?

Если это возможно сделать в более поздних версиях Visual Studio, я хотел бы услышать об этом.

Установка точки останова на следующей строке кода, который имеет условное ломаться только для этой нити не ответ я ищу, так как это слишком много работы, чтобы быть полезным для меня :)

ответ

37

Я думаю, что на ваш вопрос есть только один ответ, который вы считаете «слишком много работающим». Однако я считаю, что это потому, что вы идете по этому пути неправильно. Позвольте мне представить шаги для добавления условной точки останова на Thread ID, которые чрезвычайно просты, но не очевидны, пока вы их не узнаете.

  1. Остановка отладчика в точке, где вы находитесь в правильном потоке вы хотите продолжить отладку в (который я бы догадаться, как правило, первый поток, который попадает туда).

  2. Введите $TID в окно с часами.

  3. Добавить точку останова с условием $TID == <стоимостью $ TID от Watch Window>,
    Пример: $TID == 0x000016a0

  4. Продолжить выполнение.

$TID волшебная переменная для компиляторов Microsoft (так, по крайней мере Визуальная   студии   2003), который имеет значение текущего Thread ID. Это делает его намного проще, чем смотреть (FS + 0x18) [0x24]. = D

При этом вы можете получить то же поведение, что и контрольные точки One-Shot отладчика с помощью некоторых простых макросов. Когда вы перешагиваетесь, отладчик, за кулисами, устанавливает точку останова, переходит к этой точке останова, а затем удаляет ее. Ключ к последовательному пользовательскому интерфейсу удаляет эти контрольные точки, если удалена точка останова . ЛЮБОЙ.

Следующие два макроса обеспечивают Step Over и Run To Cursor для текущего потока. Это выполняется так же, как и отладчик, причем точки останова удаляются после выполнения, независимо от того, какая точка останова ударяется.

Вам необходимо назначить комбинацию клавиш для их запуска.

Примечание: Один нюанс - Шаг За макросъемки работает только правильно, если курсор находится на линии, которую вы хотите перешагнуть. Это связано с тем, что он определяет текущее местоположение по местоположению курсора и просто добавляет его к номеру строки. Возможно, вы сможете заменить расчет местоположения информацией о текущей точке выполнения, хотя мне не удалось найти эту информацию в среде Macro IDE.

Здесь они и охота на удачу!

Для использования этих макросов в Visual Studio:
1. Откройте Macro IDE (из меню выберите: Tools-> Macros-> Macro IDE ...)
2. Добавить новый код файла (из меню: выберите: Проект-> Добавить новый элемент ..., выберите код файла и нажмите Добавить)
3. Вставить в этом коде.
4. Сохраните файл.

Чтобы добавить комбинации клавиш для запуска макросов в Visual Studio:
1. Откройте параметры (из меню, выберите: Сервис-> Параметры)
2. Заменяется > Клавиатура для окружающей среды
3. В Показать команды, содержащие:, тип Макросы. чтобы увидеть все ваши макросы.
4. Выберите макрос, а затем нажмите на Пресс горячих клавиш:
5. Тип комбо вы хотите использовать (забой удаляет набранные комбо)
6. нажмите Присвоить, чтобы установить ярлык для запуска выбранный макрос.

Imports System 
Imports EnvDTE 
Imports EnvDTE80 
Imports System.Diagnostics 

Public Module DebugHelperFunctions 

    Sub RunToCursorInMyThread() 
     Dim textSelection As EnvDTE.TextSelection 
     Dim myThread As EnvDTE.Thread 
     Dim bp As EnvDTE.Breakpoint 
     Dim bps As EnvDTE.Breakpoints 

     ' For Breakpoints.Add() 
     Dim FileName As String 
     Dim LineNumber As Integer 
     Dim ThreadID As String 

     ' Get local references for ease of use 
     myThread = DTE.Debugger.CurrentThread 
     textSelection = DTE.ActiveDocument.Selection 

     LineNumber = textSelection.ActivePoint.Line 
     FileName = textSelection.DTE.ActiveDocument.FullName 
     ThreadID = myThread.ID 

     ' Add a "One-Shot" Breakpoint in current file on current line for current thread 
     bps = DTE.Debugger.Breakpoints.Add("", FileName, LineNumber, 1, "$TID == " & ThreadID) 

     ' Run to the next stop 
     DTE.Debugger.Go(True) 

     ' Remove our "One-Shot" Breakpoint 
     For Each bp In bps 
      bp.Delete() 
     Next 
    End Sub 

    Sub StepOverInMyThread() 
     Dim textSelection As EnvDTE.TextSelection 
     Dim myThread As EnvDTE.Thread 
     Dim bp As EnvDTE.Breakpoint 
     Dim bps As EnvDTE.Breakpoints 

     ' For Breakpoints.Add() 
     Dim FileName As String 
     Dim LineNumber As Integer 
     Dim ThreadID As String 

     ' Get local references for ease of use 
     myThread = DTE.Debugger.CurrentThread 
     textSelection = DTE.ActiveDocument.Selection 

     LineNumber = textSelection.ActivePoint.Line 
     FileName = textSelection.DTE.ActiveDocument.FullName 
     ThreadID = myThread.ID 
     LineNumber = LineNumber + 1 

     ' Add a "One-Shot" Breakpoint in current file on current line for current thread 
     bps = DTE.Debugger.Breakpoints.Add("", FileName, LineNumber, 1, "$TID == " & ThreadID) 

     ' Run to the next stop 
     DTE.Debugger.Go(True) 

     ' Remove our "One-Shot" Breakpoint 
     For Each bp In bps 
      bp.Delete() 
     Next 
    End Sub 


End Module 

Отказ от ответственности: Я написал эти макросы в Visual Studio 2005. Вероятно, вы можете использовать их в Visual Studio 2008. Они могут потребовать изменения для Visual Studio 2003 и ранее.

+0

Это работает для всех? Я не мог заставить его работать. –

+0

Это работает для меня. = D В чем была ваша ошибка? – Aaron

+0

@Aaron: Я использую VS 2008, когда я ввожу $ TID в окне Watch. Я получаю ошибку «Имя не существует в текущем контексте». – akif

21

Вы можете заморозить другой поток или переключиться на другой поток, используя окно отладки Threads (Ctrl + Alt + H).

+0

Спасибо. Это самое близкое к хорошему решению для этого, которое я видел до сих пор! – Laserallan

+4

Вот что я делаю, вопрос в том, почему ... почему один шаг изменяет выполняемый поток? –

6

Простой способ отладки одного конкретного потока - заморозить все остальные потоки из окна Threads.

+3

Нет ли способа просто заставить VS не переключать потоки? В какой ситуации даже смысл отладчика переключиться на другой поток, когда вы переходите через линию и ожидаете, что просто придете к следующему? – stu

+0

У кого-нибудь есть макрос для этого?Было бы неплохо, если бы плагин или макрос достигли нажатия stepin/over заморозить все остальные потоки, проследить линию, затем разморозить все остальные потоки. Это эффективно то, что я хочу сделать, должен быть способ автоматизировать это. – stu

+0

Конечно, это должно правильно работать в визуальной студии в первую очередь. – stu

2

[Ctrl + D, T] или [Ctrl + Alt + H] - Открывает окно Thread (используется для контроля, замораживания и название темы)

Окно нить позволяет выбрать, если вы хотите показать расположение других потоков в визуальной студии. Это хорошее напоминание мне, что текущий поток, который я отлаживаю, не единственный в игре. Наведение курсора на маркер потока дает вам имя и идентификатор потоков.

Другие советы по адресу: http://devpinoy.org/blogs/jakelite/archive/2009/01/10/5-tips-on-debugging-multi-threaded-code-in-visual-studio-net.aspx

2

Очевидно, что Visual Studio 2010 переходит только другие потоки, если нажать F10 когда отладчик должен был разорвать в этом потоке до или если точка останова, которая будет хит в этой теме.

Я использовал следующий код для проверки поведения:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var t = new Thread(new ThreadStart(Work)); 
     t.Start(); 

     for (int i = 0; i < 20; i++) 
     { 
      Thread.Sleep(1000); 
      Console.WriteLine("............"); 
     } 

     t.Join(); 
    } 

    static void Work() 
    { 
     for (int i = 0; i < 20; i++) 
     { 
      Thread.Sleep(1000); 
      Console.WriteLine("ZZzzzzzzzzzzzzzz"); 
     } 
    } 
} 

Если вы просто stept к программе или добавить точку останова в методе Main(), попав F10 только шаги, с помощью кода из главного потока ,

Если вы добавили точку останова в методе Work(), отладчик выполнит оба потока.

Такое поведение Visual Studio имеет смысл, но все это, кажется, как документировано мне ...

1

Я недавно была такая же проблема отладки только определенный поток. Хотя я не буду упускать из виду вышеупомянутый ответ как очень всеобъемлющий и ценный (и хотел бы, чтобы я нашел его 2 дня назад) - я применил следующее, чтобы помочь «повседневному» устранению неполадок.

Эта идея является всего лишь обходным решением при запуске нескольких экземпляров одного и того же класса в нескольких потоках, которые всегда применяются нашим приложением.

Мы инициализируем все экземпляры классов уникальным идентификатором или именем, поэтому мы знаем, как и почему создавался экземпляр. Тогда, когда нам нужно отладить конкретный экземпляр (то есть нить) - и не замерзнуть другие темы, мы добавим следующее:

if (instance.ID == myID) 
{ 
    // Assert BreakPoint 
} 

В нашем случае, мы устанавливаем фиксированные идентификаторы, так что мы знаем ID мы хотим если у нас возникла проблема.

Смежные вопросы