Подтвердить что ты не робот

Как обнаружить и отладить многопоточные проблемы?

Это продолжение этого вопроса, где я не получил никакого ввода по этому вопросу. Вот краткий вопрос:

Можно ли обнаруживать и отлаживать проблемы, возникающие из многопоточного кода?

Часто мы должны сообщать нашим клиентам: "Мы не можем воспроизвести проблему здесь, поэтому мы не можем ее исправить. Расскажите нам о шагах по воспроизведению проблемы, тогда мы ее исправим". Это какой-то неприятный ответ, если я знаю, что это проблема многопоточности, но в основном я этого не делаю. Как узнать, что проблема - проблема многопоточности и как ее отладить?

Я хотел бы знать, есть ли какие-либо специальные фреймворки регистрации, методы отладки или инспекторы кода или что-то еще, чтобы помочь решить такие проблемы. Приветствуем общие подходы. Если какой-либо ответ должен быть связан с языком, сохраните его на .NET и Java.

4b9b3361

Ответ 1

Общеизвестно, что проблемы с многопоточностью/параллелизмом трудно воспроизвести - это одна из причин, по которой вам следует разрабатывать, чтобы избежать или хотя бы минимизировать вероятности. По этой причине неизменные объекты так ценны. Попробуйте изолировать изменяемые объекты в одном потоке, а затем тщательно контролировать обмен изменяемыми объектами между потоками. Попытка программировать с дизайном передачи объекта, а не "общими" объектами. В последнем случае используйте полностью синхронизированные объекты управления (о которых легче рассуждать) и избегайте использования синхронизированных объектов других объектов, которые также должны быть синхронизированы, то есть пытайтесь сохранять их самодостаточными. Ваша лучшая защита - это хороший дизайн.

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

Живые блокировки сложнее - лучше всего наблюдать за системой, находясь в состоянии ошибки.

Расовые условия, как правило, чрезвычайно трудно воспроизвести, и еще сложнее определить их при ручной проверке кода. С этим путем, который я обычно выбираю, помимо обширного тестирования для репликации, является рассуждение о возможностях и попытка записать информацию, чтобы доказать или опровергнуть теории. Если у вас есть прямые доказательства коррупции в государстве, вы можете рассуждать о возможных причинах, основанных на коррупции.

Чем сложнее система, тем сложнее находить ошибки параллелизма и рассуждать о ее поведении. Используйте такие инструменты, как JVisualVM и профилировщики удаленного подключения - они могут спасти жизнь, если вы можете подключиться к системе в состоянии ошибки и проверить потоки и объекты.

Также обратите внимание на различия в возможном поведении, которые зависят от количества ядер ЦП, конвейеров, пропускной способности шины и т.д. Изменения в оборудовании могут повлиять на вашу способность воспроизвести проблему. Некоторые проблемы будут отображаться только на одноядерных процессорах, другие только на многоядерных.

И последнее: попробуйте использовать объекты параллелизма, распространяемые вместе с системными библиотеками - например, в Java ваш друг - java.util.concurrent. Написание собственных объектов управления параллелизмом сложно и чревато опасностью; оставьте это экспертам, если у вас есть выбор.

Ответ 2

Я думал, что ответ, который вы получили на ваш другой вопрос, был довольно хорош. Но я остановлюсь на этих моментах.

Только изменение общего состояния в критическом разделе (взаимное исключение)

Приобретайте блокировки в заданном порядке и освобождайте их в обратном порядке.

Использовать заранее созданные абстракции, когда это возможно (Как и материал в java.util.concurrent)

Кроме того, некоторые инструменты анализа могут выявлять некоторые потенциальные проблемы. Например, FindBugs может найти некоторые проблемы с потоками в программах Java. Такие инструменты не могут найти все проблемы (они не серебряные пули), но они могут помочь.

Как vanslly указывает на комментарий к этому ответу, изучение хорошо размещенного вывода журнала также может очень полезно, но остерегайтесь Heisenbugs.

Ответ 3

Предполагая, что у меня есть сообщения о проблемах, которые трудно воспроизвести, я всегда нахожу их, читая код, предпочтительно, используя чтение пара-кода, поэтому вы можете обсудить потребности семантики/блокировки потоков. Когда мы делаем это на основе сообщенной проблемы, я считаю, что мы всегда сталкиваемся с одной или несколькими проблемами довольно быстро. Я думаю, что это также довольно дешевая техника для решения сложных проблем.

Извините за невозможность сказать вам нажать ctrl + shift + f13, но я не думаю, что есть что-то подобное. Но просто думать о том, что сообщение о проблеме на самом деле обычно дает довольно сильное чувство направления в коде, так что вам не нужно начинать с main().

Ответ 4

В дополнение к другим хорошим ответам, которые вы уже получили: всегда проверяйте на машине с по меньшей мере столько процессоров/процессорных ядер, сколько клиент использует, или как в вашей программе есть активные потоки. В противном случае некоторые многопоточные ошибки могут быть трудно воспроизвести.

Ответ 5

Помимо аварийных дампов, техникой является обширное ведение журнала во время выполнения: где каждый поток регистрирует, что он делает.

Первый вопрос, когда сообщается об ошибке, может быть: "Где файл журнала?"

Иногда вы можете увидеть проблему в файле журнала: "Этот поток обнаруживает здесь незаконное/неожиданное состояние... и смотри, этот другой поток делал это, как раз перед, так и после этого".

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

Ответ 6

Для Java существует инструмент проверки под названием javapathfinder, который, по моему мнению, полезен для отладки и проверки многопоточного приложения на предмет возможных состояний гонки и ошибок блокировки из кода.
Он отлично работает как с Eclipse, так и с IDE Netbean.

[2019] хранилище githubhttps://github.com/javapathfinder

Ответ 7

Иногда многопоточные решения нельзя избежать. Если есть ошибка, ее необходимо исследовать в режиме реального времени, что почти невозможно для большинства инструментов, таких как Visual Studio. Единственное практическое решение - писать трассы, хотя сама трассировка должна:

  • не добавлять задержку
  • не использовать блокировку
  • быть многопоточным безопасным.
  • проследите, что произошло в правильной последовательности.

Это звучит как невозможная задача, но ее легко достичь, записав трассировку в память. В С# он будет выглядеть примерно так:

public const int MaxMessages = 0x100;
string[] messages = new string[MaxMessages];
int messagesIndex = -1;

public void Trace(string message) {
  int thisIndex = Interlocked.Increment(ref messagesIndex);
  messages[thisIndex] = message;
}

Метод Trace() является многопоточным безопасным, не блокирующим и может быть вызван из любого потока. На моем ПК требуется около 2 микросекунд, что должно быть достаточно быстрым.

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

Более подробное описание этого подхода, который также собирает информацию о потоке и времени, перерабатывает буфер и выводит трассировку, которую вы можете найти: CodeProject: отладка многопоточного кода в режиме реального времени 1

Ответ 8

Небольшая диаграмма с некоторыми методами отладки, которые нужно учитывать при отладке многопоточного кода. График растет, пожалуйста, оставляйте комментарии и советы для добавления. (обновить файл на эта ссылка)

Multithreaded debugging chart

Ответ 9

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

Я использовал WinDbg + SoS для проблем с потоками в .NET-коде. Вы можете проверить блокировки (sync blokcs), стеки вызовов потоков и т.д.

Ответ 10

Блог Tess Ferrandez содержит хорошие примеры использования WinDbg для отладки тупиков в .NET.

Ответ 11

assert() - ваш друг для определения условий гонки. Всякий раз, когда вы входите в критический раздел, утверждайте, что связанный с ним инвариант является истинным (для чего CS). Хотя, к сожалению, проверка может быть дорогостоящей и, следовательно, не подходит для использования в производственной среде.

Ответ 12

Я реализовал инструмент vmlens для определения условий гонки в программах Java во время выполнения. Он реализует алгоритм eraser.

Ответ 14

Я столкнулся с проблемой нити, которая давала ТАМЫЙ неправильный результат и не вела себя непредсказуемо, поскольку каждый раз другие условия (память, планировщик, загрузка) были более или менее одинаковыми.

По моему опыту, я могу сказать, что HARDEST PART - это признать проблему с потоком, и BEST SOLUTION - внимательно рассмотреть многопоточный код. Просто взглянув на код потока, вы должны попытаться выяснить, что может пойти не так. Другие пути (дамп потока, профайлер и т.д.) Будут вторыми.

Ответ 15

Ограничьте функции, которые вызываются, и исключите, что можно, а что нельзя. Когда вы обнаружите фрагменты кода, которые, как вы подозреваете, могут вызывать проблему, добавьте к ней множество подробных журналов/трассировок. Когда проблема возникнет снова, просмотрите журналы, чтобы увидеть, как код выполняется не так, как в "базовых" ситуациях.

Если вы используете Visual Studio, вы также можете установить точки останова и использовать окно Parallel Stacks. Parallel Stacks - это огромная помощь при отладке параллельного кода, которая дает вам возможность переключаться между потоками для их независимой отладки. Больше info-

https://docs.microsoft.com/en-us/visualstudio/debugger/using-the-parallel-stacks-window?view=vs-2019

https://docs.microsoft.com/en-us/visualstudio/debugger/walkthrough-debugging-a-parallel-application?view=vs-2019

Ответ 16

Я использую GNU и использую простой script

$more gdb_tracer

b func.cpp:2871
r
#c
while (1)
next
#step
end

Ответ 17

Лучшее, что я могу придумать, - избегать многопоточного кода, когда это возможно. Кажется, очень мало программистов, которые могут писать многопоточные приложения без ошибок, и я бы сказал, что нет кодеров, которые могли бы писать безболезненные большие многопоточные приложения.