Я думаю, что интервью задает вам вопрос об уловке. Если бы вы могли использовать статический анализ, чтобы предотвратить тупик ... никто бы не зашел в тупик!
Лично, когда я ищу тупик, я начинаю с поиска функций, где критический участок больше, чем вызов функции. Например
void func(){
lock(_lock){
func2();
}
}
Это не совсем ясно, что func2
делает. Возможно, он отправляет событие в тот же поток, что означает, что это событие все еще является частью критического раздела. Возможно, тогда он запирает разный замок. Может быть, он отправляется в threadpool и больше не возвращается, потому что теперь он в другой теме! В таких местах вы можете начать видеть сценарии взаимоблокировки: когда у вас несколько мест размещения без реентера.
В других случаях, когда отслеживаются сценарии тупиковой ситуации, я возвращаюсь назад, и я пытаюсь найти, где созданы все потоки. Я придумываю каждую функцию и там, где она может быть запущена. Если вы не уверены, добавление журнала для входа в журнал, откуда пришел вызов функции, также может помочь.
Вы также можете избежать взаимоблокировок, используя блокирующие данные (но для этого требуется столько же, сколько использовать). Вы хотите минимизировать доступ к структуре блокировки, потому что каждый раз, когда вы обращаетесь к ней, он может измениться.
Как уже упоминалось в другом ответе, вы можете использовать мьютексы с таймаутами, но это не гарантирует всегда работу (что, если ваш код должен работать дольше, чем тайм-аут?). В другом комментарии упоминалось, что это, возможно, то, о чем просил интервьюер. Я нахожу, что в производстве это не очень хорошая идея. Таймауты меняются все время, возможно, что-то заняло больше времени, чем ожидалось, и ударил таймаут. Я думаю, что лучше позволить ему зайти в тупик, взять свалку процесса, а затем найти именно то, что удерживало блокировки и устранить проблему. Конечно, если ваши бизнес-требования не позволяют этого, тогда вы можете использовать это как часть стратегии защитного кодирования наряду с выборами для размещения смарт-блокировок.
Я не согласен с вашим интервью, что замки всегда добавляют огромную проблему с производительностью. Неисправные блокировки/мьютексы/и т. Д. Первый тест в качестве спин-блокировки перед передачей ОС и шпиндельных замков дешевый.
В общем, лучший способ избежать тупиковой ситуации - понять ваш поток программ. Каждый раз, когда вы вводите новый объект блокировки, подумайте о том, где он используется и что использует его в цепочке.
Вы должны проанализировать запирающие модели ... Не думаю, очень компактный ответ. –
Он просил общий способ проверить тупик? Все, что я знаю, - это посмотреть на код и использовать некоторый интеллект ... – TheKingDave
Чтобы предотвратить взаимоблокировки, вам нужно использовать реализацию lockfree. Если вам абсолютно необходимо хранить блокировки, вы должны убедиться, что когда требуется несколько блокировок, блокируемые объекты блокируются в определенном порядке. – Nolonar