Как обнаружить и устранить проблемы с многопоточностью? - PullRequest
62 голосов
/ 01 февраля 2009

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

Возможно ли обнаружение и устранение проблем, связанных с многопоточным кодом?

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

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

Ответы [ 16 ]

77 голосов
/ 01 февраля 2009

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

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

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

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

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

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

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

7 голосов
/ 01 февраля 2009

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

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

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

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

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

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

5 голосов
/ 08 ноября 2012

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

5 голосов
/ 01 февраля 2009

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

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

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

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

5 голосов
/ 01 февраля 2009

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

5 голосов
/ 01 февраля 2009

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

Извините, что не смог сказать вам нажать Ctrl + Shift + F13, но я не думаю, что есть что-то подобное. Но просто размышление о о том, что о проблеме, о которой сообщают, на самом деле составляет , обычно дает довольно сильное чувство направления в коде, поэтому вам не нужно начинать с main ().

3 голосов
/ 03 июля 2014

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

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

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

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

2 голосов
/ 11 декабря 2014

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

Multithreaded debugging chart

1 голос
/ 15 декабря 2013

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

1 голос
/ 01 февраля 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...