Какие уроки вы извлекли из проекта, который почти / фактически провалился из-за плохой многопоточности?
Иногда структура навязывает определенную модель потоков, которая усложняет процесс на порядок.
Что касается меня, мне еще предстоит оправиться от последнего сбоя, и я чувствую, что для меня лучше не работать над чем-либо, что связано с многопоточностью в этой среде.
Я обнаружил, что хорошо справляюсь с проблемами многопоточности, которые имеют простое форк / соединение и где данные перемещаются только в одном направлении (тогда как сигналы могут двигаться в круговом направлении).
Я не могу обработать графический интерфейс, в котором некоторую работу можно выполнять только в строго сериализованном потоке («основной поток»), а другую работу можно выполнять только в любом потоке, кроме основного потока («рабочие потоки»), и где данные и сообщения должны перемещаться во всех направлениях между N компонентами (полностью связанный график).
В то время, когда я оставил этот проект для другого, повсюду были проблемы с тупиком. Я слышал, что через 2-3 месяца нескольким другим разработчикам удалось устранить все проблемы взаимоблокировки до такой степени, что они могут быть отправлены клиентам. Мне так и не удалось обнаружить ту недостающую часть знаний, которой мне не хватает.
Кое-что о проекте: число идентификаторов сообщений (целочисленные значения, которые описывают значение события, которое может быть отправлено в очередь сообщений другого объекта, независимо от потоков), исчисляется несколькими тысячами. Уникальные строки (пользовательские сообщения) также встречаются около тысячи.
добавленной
Лучшая аналогия, которую я получил от другой команды (не связанной с моими прошлыми или настоящими проектами), состояла в том, чтобы «поместить данные в базу данных». («База данных» относится к централизации и элементарным обновлениям.) В графическом интерфейсе пользователя, который фрагментирован на несколько представлений, выполняющихся в одном и том же «главном потоке», и вся тяжелая работа без графического интерфейса выполняется в отдельных рабочих потоках, данные приложения должны храниться в одном месте, которое действует как база данных, и позволяет «базе данных» обрабатывать все «атомарные обновления», связанные с нетривиальными зависимостями данных. Все остальные части GUI просто обрабатывают рисование экрана и ничего больше. Части пользовательского интерфейса могут кешировать содержимое, и пользователь не заметит, если он устарел на долю секунды, если он спроектирован правильно. Эта «база данных» также известна как «документ» в архитектуре Document-View. К сожалению - нет, мое приложение фактически хранит все данные в представлениях. Я не знаю, почему это так.
Другие участники:
(Участникам не нужно использовать реальные / личные примеры. Уроки из отдельных примеров, если вы считаете, что они заслуживают доверия, также приветствуются.)
Ответы:
Мой любимый урок - очень тяжело победил! - в том, что в многопоточной программе планировщик - это хитрая свинья, которая вас ненавидит. Если что-то может пойти не так, как надо, но неожиданным образом. Получите что-то не так, и вы будете гоняться за странными heisenbugs (потому что любой инструмент, который вы добавите, изменит время и даст вам другой способ запуска).
Единственный разумный способ исправить это состоит в том, чтобы строго собрать весь поток обработки в виде небольшого фрагмента кода, который все делает правильно и который очень консервативен в отношении обеспечения правильного удержания блокировок (и с глобально постоянным порядком получения также) , Самый простой способ сделать это - не делить память (или другие ресурсы) между потоками, за исключением обмена сообщениями, который должен быть асинхронным; это позволяет вам писать все остальное в стиле, не обращающем внимания на потоки. (Бонус: масштабирование на несколько машин в кластере намного проще.)
источник
is that in a multithreaded program the scheduler is a sneaky swine that hates you.
- нет, это не так, он делает именно то, что вы сказали, чтобы сделать :)Вот несколько основных уроков, которые я могу вспомнить прямо сейчас (не из проектов, терпящих неудачу, а из реальных проблем, наблюдаемых в реальных проектах):
источник
Мы унаследовали часть, где проект GUI использует дюжину потоков. Это не дает ничего, кроме проблем. Тупики, проблемы с гонками, вызовы GUI с несколькими потоками ...
источник
В Java 5 и более поздних версиях есть Executors, которые призваны облегчить работу с многопоточными программами в стиле fork-join.
Используйте их, это снимет много боли.
(и, да, это я узнал из проекта :))
источник
У меня есть опыт работы со сложными встроенными системами реального времени. Вы не можете проверить отсутствие проблем, вызванных многопоточностью. (Иногда можно подтвердить наличие). Код должен быть доказуемо правильным. Так что лучшая практика вокруг любого и всех потоков взаимодействия.
источник
Аналогия с уроком по многопоточности, который я провел в прошлом году, была очень полезной. Синхронизация потоков подобна сигналу трафика, защищающему перекресток (данные) от одновременного использования двумя машинами (потоками). Ошибка многих разработчиков заключается в том, что в большей части города они загораются красным светом, чтобы пропустить один автомобиль, потому что они считают, что слишком сложно или опасно определить точный сигнал, который им нужен. Это может хорошо работать при слабом трафике, но приведет к блокировке по мере роста вашего приложения.
Это то, что я уже знал в теории, но после этого урока аналогия действительно застряла у меня, и я был удивлен, как часто после этого я буду исследовать проблему с многопоточностью и находить одну гигантскую очередь, или прерывания прерывания повсеместно отключаются во время записи в переменную использовались только два потока, или мьютексы удерживались долгое время, когда его можно было бы реорганизовать, чтобы вообще избежать
Другими словами, некоторые из худших проблем с потоками вызваны излишним стремлением избежать проблем с потоками.
источник
Попробуйте сделать это снова.
По крайней мере для меня то, что создавало разницу, было практикой. После выполнения многопоточной и распределенной работы довольно много раз, вы просто овладеваете ею.
Я думаю, что отладка - это то, что действительно затрудняет. Я могу отлаживать многопоточный код, используя VS, но я действительно в полной растерянности, если мне придется использовать GDB. Моя вина, наверное.
Еще одна вещь, о которой узнают больше, - это блокировка структур данных.
Я думаю, что этот вопрос может быть действительно улучшен, если вы укажете рамки. Например, пулы потоков .NET и фоновые рабочие действительно отличаются от QThread. Всегда есть несколько специфичных для платформы ошибок.
источник
Я узнал, что обратные вызовы от модулей более низкого уровня к модулям более высокого уровня являются огромным злом, потому что они вызывают получение блокировок в обратном порядке.
источник