2013-03-12 3 views
15

У меня было интервью всего 5 минут назад, я не ответил на 3 вопроса, может кто-то мне помочь.Как найти тупик и предотвратить его в C#

Вопрос:

Как искать сценарии тупиковых в функции приложения многопоточных и предотвратить его?

Ответ Я дал:

Я дал определение тупике и замок, мьютекса, монитор, семафора. Он сказал мне, что эти инструменты, но как искать сценарий тупиковый, как потому, что когда мы используем эти инструменты вслепую, он стоит на производительность он сказал :(

Пожалуйста, помогите мне понять это.

+3

Вы должны проанализировать запирающие модели ... Не думаю, очень компактный ответ. –

+2

Он просил общий способ проверить тупик? Все, что я знаю, - это посмотреть на код и использовать некоторый интеллект ... – TheKingDave

+0

Чтобы предотвратить взаимоблокировки, вам нужно использовать реализацию lockfree. Если вам абсолютно необходимо хранить блокировки, вы должны убедиться, что когда требуется несколько блокировок, блокируемые объекты блокируются в определенном порядке. – Nolonar

ответ

5

Инструменты анализа производительности также могут быть полезны при определении взаимоблокировок, в частности. Этот вопрос даст некоторое представление в этой теме: C#/.NET analysis tool to find race conditions/deadlocks.

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

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

Блокировка чего-то только ради блокировки влияет на производительность, так как поток ждет друг друга. Вы должны проанализировать рабочий процесс, чтобы определить, что действительно нужно заблокировать, когда с каким типом блокировки (простой lock или, возможно, Readerwriterlockslim). Существует множество типичных способов предотвращения взаимоблокировки.

Например, при использовании Readerwriterlockslim вы можете использовать тайм-аут, чтобы предотвратить тупики (если вы будете ждать много, вы Абор aquiring замка) http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx

if (cacheLock.TryEnterWriteLock(timeout)) 
{ 
... 
} 

И вы должны быть в состоянии предложить такие тайм-ауты.

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

Тема очень большая ... вы можете продолжать и продолжать об этом. Но без определенности. Зная, что такое блокировка, и зная использовать блокировки/семафоры/мьютекс в широкомасштабных многозадачных приложениях, - это две разные вещи.

+0

Спасибо, Coral, это новое знание для меня о знании инструментов для анализа производительности, взглянет на эту ссылку :) Еще раз спасибо ... Cheers – Learner

0

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

Mutex m; // similar overloads exist for all locking primitives 
if (!m.WaitOne(TimeSpan.FromSeconds(30))) 
    throw new Exception("Potential deadlock detected."); 

Когда WaitOne вернется false, он будет ждать 30 секунд, а замок Я не был бы выпущен. Если вы знаете, что все заблокированные операции должны завершаться в течение миллисекунд (если нет, то просто увеличивайте время ожидания), это очень хороший признак того, что что-то пошло не так.

+0

Как это говорит о разнице между тупиком и голодом ? – Nolonar

+0

Привет, Hnagis, спасибо за ответ, я боюсь, что не понял, что вы говорите :(Я новичок в многопоточности :(Прошу прощения, это будет здорово, если вы можете перефразировать то, что вы пытаются сказать :( – Learner

+1

Он устанавливает тайм-аут на мьютексе. Это означает, что он будет ждать до 30 секунд, прежде чем продолжить. Если он истечет, функция вернет значение false, и поэтому он может знать, что никто не пульсировал мьютекс. При достаточно длинном таймауте это * могло означать тупик. Могло бы быть ключевое слово здесь. Это не всегда означает тупик. – devshorts

4

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

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

void func(){ 
    lock(_lock){ 
     func2(); 
    } 
} 

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

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

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

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

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

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

+0

Спасибо за ваше объяснение, его большое количество информации для меня :) Cheers – Learner

6

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

Тупик возникает, когда каждая из двух (минимум двух) потоков пытается получить блокировку на ресурсе, уже заблокированном другим. Тема 1 заблокирована на ресурсах 1 пытается получить блокировку ресурса 2. В то же время в Thread 2 есть блокировка ресурса 2, и он пытается получить блокировку для ресурса 1. Два потока никогда не отказываются от своих блокировок, поэтому происходит DEADLOCK ,

Самый простой способ избежать тупиковой ситуации - использовать значение таймаута. Класс Monitor (system.Threading.Monitor) может установить таймаут во время покупки блокировки.

Пример

try{ 
    if(Monitor.TryEnter(this, 500)) 
    { 
     // critical section 
    } 
} 
catch (Exception ex) 
{ 

} 
finally 
{ 
    Monitor.Exit(); 
} 

Read More

+0

Спасибо, Гарре, что поможет мне, будет больше изучать это :) Cheers – Learner

+0

Просто из любопытства, как получилось то, что вы используете catch after if block? Или это просто своего рода псевдокод. В winCE (.net compacFframework) я мог передать только объект без таймаута в Monitor.TryEnter. Есть ли способ написать собственную реализацию с тайм-аутом? –

+0

@RomaBorodov Похоже, я забыл попробовать. Хороший улов! Исправлена. –

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