#c# #multithreading
#c# #многопоточность
Вопрос:
У меня проблема, я не могу решить эту проблему
void Transfer(Account a, Account b, decimal amount)
{
lock (a) {
lock (b) {
if (a.Balance < amount)
throw new InsufficientFundsExc();
a.Balance -= amount;
b.Balance = amount;
}
}
}
и вопрос в том, » это перевод между банковскими счетами. структура «lock (…)» используется в условиях гонки. В чем эта проблема? и какое решение вы предлагаете? ВЫ МОЖЕТЕ МНЕ ПОМОЧЬ?
Комментарии:
1. Мой вопрос: «это домашнее задание?»
2. Что вы рассмотрели и где вы застряли? Можете ли вы предоставить данные о ваших попытках ее решить?
3. Я добавил тег homework. @user744303: Пожалуйста, удалите этот тег, если это не домашнее задание
4. Почему вы многопоточны в первую очередь? По моему опыту, многопоточность дорогостоящих вычислений иногда полезна, но многопоточность всего домена обычно является плохой идеей.
5. Почему это относится к c #, java, c и vb?
Ответ №1:
если у вас есть передача из A в B одновременно с передачей из B в A, это может привести к взаимоблокировке, потому что у вас нет порядка блокировки.
- Поток 1 блокирует
- Поток 2 блокирует B
- Поток 1 ожидает на B
- Поток 2 ожидает на
- мертвый
Но почему, черт возьми, этот код вообще многопоточный?
Вам нужно всегда использовать свои блокировки в одном и том же порядке. Например, присваивая каждой блокировке целочисленный идентификатор и всегда сначала блокируя нижний идентификатор.
Комментарии:
1. Пожалуйста, не могли бы вы объяснить это более подробно и посоветовать мне, как это решить
2. Выполняйте свои блокировки всегда в одном и том же порядке. Или просто отказаться от многопоточности все вместе. Это не похоже на ситуацию, когда многопоточность необходима в первую очередь.
3. Кроме того, обычно это плохая практика или, по крайней мере, подвержено ошибкам (и часто является признаком слабого дизайна) — блокировать ресурсы, которыми вы не владеете полностью, и инкапсулировать. В этом примере
Transfer
метод демонстрирует эту проблему, потому что его трудно реализовать без блокировки двух ресурсов, которые логически не имеют одного и того же владельца.4. Если это домашнее задание, сделайте это сами. Если это не домашнее задание, а реальная проблема, выбросьте многопоточность. Я не буду делать за вас домашнюю работу.
5. Тогда вам следует либо много узнать о многопоточности, либо вообще не использовать ее. Многопоточность сложна, легко допустить ошибки, которые возникают случайным образом, и это редко бывает необходимо. Я рекомендую выборочно использовать многопоточность при дорогостоящих вычислениях.
Ответ №2:
Если вы можете упорядочить учетные записи (по идентификационному номеру или чему-то еще), вы всегда можете заблокировать сначала ту, у которой меньший идентификатор. Это гарантирует, что ни один поток не попытается заблокировать a
then b
одновременно с блокировкой другого потока a
then b
, поскольку они оба будут заблокированы a
первыми.
Ответ №3:
взаимоблокировка
когда Transfer (a, b, 10.0); и Transfer (b, a, 10.0); вызываются одновременно, первый вызов блокирует a, затем второй может заблокировать b до того, как первый сможет заблокировать a, и ни один из них не может продолжаться -> взаимоблокировка
Ответ №4:
Как предполагает CodeInChaos, ваша проблема является потенциальной взаимоблокировкой. Если вы не понимаете, что такое взаимоблокировка, я рекомендую прочитать проблему с обедающими философами в Википедии.
В статье объясняется проблема в реальных терминах и предлагаются некоторые решения, включая использование порядка блокировки.
Редактировать:
Чтобы еще больше связать вашу проблему со статьей, банковские счета являются форками (т. Е. разделяемыми ресурсами, которые могут использоваться исключительно, поэтому должны быть заблокированы), а потоки, обращающиеся к коду, являются философами (т. Е. Объектами, которым время от времени требуется использование блокируемых ресурсов).).
Ответ №5:
@RatchetFreak попал в самую точку своим ответом о том, почему это произошло. Я думаю, что основная проблема, с которой вы сталкиваетесь, заключается в том, что блокировка — это конструкция очень низкого уровня, которая затрудняет разумное представление о потоковом сценарии.
Я бы рекомендовал (если возможно) использовать набор конструкций немного более высокого уровня, чтобы облегчить себе жизнь. У Дэниела Чемберса есть хороший легкий набор утилит для этого в его библиотеке.
Ответ №6:
Представьте, что две очень упрямые дамы отправились в дом отдыха со своими мужьями. Они проснулись на следующее утро и обнаружили, что люди ушли. Осталась только одежда: юбка и рубашка (кажется, одному из мужчин нравилось носить женскую одежду — это могло бы многое объяснить). Дамы понимают необходимость пойти за вещами — едой, одеждой, новыми мужьями. Они оба думают «ну, я надену одежду и уйду», и один надевает юбку, в то время как другой надевает рубашку. Они видят, что произошло, но каждый слишком упрям, чтобы изменить свой план (и слишком застенчив, чтобы выйти полуголым). Их упрямство сродни тупому компьютеру, который следует программе, которая «в то время выглядела хорошо». Они не могут / не будут автоматически пересматривать кажущийся разумным подход, который у них был выше, при обнаружении «ошибки времени выполнения», поэтому они оба обречены на голодную смерть. Это мертвая блокировка: ожидание ресурсов, которые вы не можете получить, потому что кто-то ждет того, что вы перегружаете сами.
При чуть большей дальновидности и планировании они могли бы найти стратегию, которая обеспечила бы выход одного из них. Например:
- порядок, основанный на разыскиваемых объектах:
- например, только когда у вас уже есть юбка, вы можете взять рубашку (по крайней мере, с замками, один из двух потоков, оспаривающих это, может получить ее, не разрывая надвое), ИЛИ
- порядок, основанный на потенциальных владельцах:
- если оба хотят выйти в одно и то же время, тот, у кого день рождения раньше, может пойти первым, или, если то же самое, тогда тот, кто выше и т.д., Или
- оба снова отключаются и повторяют попытку после короткого, но случайного интервала
и т.д.