Единичное тестирование, блокировки и условия гонки

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

Сейчас я склоняюсь к пропуску модульных тестов и сосредоточен на стресс-тестах. Проблема заключается в том, что вы можете провести стресс-тест 5 раз и увидеть пять разных результатов.

EDIT: Я знаю его, вероятно, просто сон, но если бы был способ контролировать отдельные потоки и заставить их выполнять одну команду за раз, то я мог бы где-то добраться.

Ответ 2

Обычно можно принудительно предусмотреть условия гонки и взаимоблокировки, используя такие вещи, как ManualResetEvent, чтобы получить каждый поток в ожидаемом состоянии до его выпуска - т.е. получить поток A, чтобы блокировать и ждать сигнала... получить поток B для запроса блокировки и т.д.

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

Ответ 3

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

Вам все равно нужно писать модульные тесты, чтобы проверить правильность алгоритмов, а не стратегию блокировки.

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

Ответ 4

Вы можете написать класс блокировки, который обнаруживает потенциальные блокировки, просматривая порядок операций блокировки. Мы делаем это, имея контекст потока, который регистрирует все блокировки при их приобретении (может быть сделан только вариант DEBUG).

Идея состоит в том, чтобы создать граф, где узлы представляют блокировки и направленное ребро между A и B означает, что "блокировка A была сохранена, когда была зафиксирована блокировка B". Пусть ваша программа запускается с использованием обычных нагрузок, а затем проверяет циклы на графике. Цикл означает, что существует вероятность тупика, даже если ваш код не ударил его.

Ответ 5

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

Возможно, кто-то придумал хороший способ автоматизировать этот тип функциональности? Я мог представить, что автоматически запускает отладчик, разбивая один поток на определенной строке, позволяя другому работать до определенного условия, а затем утверждая для unit test.

Ответ 6

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

A Mark Bessey пишет, что это полезно только для создания репродукции, а не для обнаружения проблемы.