Является ли увольнение потока верным ответом на упрощение кода? - PullRequest
5 голосов
/ 14 июля 2009

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

Main()
    Read configuration
    Start listener thread
    Run GUI

Listener Thread
    While the app is running
        Wait for a new connection
        Run a client thread for the new connection

Client Thread
    Write synchronously
    Read synchronously
    ad inifinitum, or till they disconnect

Этот подход означает, что, хотя мне приходится беспокоиться о большом количестве блокировок, с потенциальными проблемами, которые связаны с этим, я избегаю большого количества кода спагетти из асинхронных вызовов и т. Д.

Немного более коварная версия вышла сегодня, когда я работал над кодом запуска. Запуск был быстрым, но он использовал ленивую загрузку для многих конфигураций, что означало, что, хотя запуск был быстрым, на самом деле подключение и использование службы было затруднено из-за задержки, в то время как загружались разные разделы (это было фактически измеримо в реальном время, иногда до 3-10 секунд). Таким образом, я перешел к другой стратегии: при запуске перебираю все и заставляю ленивую загрузку запускаться ... но это заставляло ее запускаться слишком медленно; вставай, иди кофе медленно. Окончательное решение: закинуть петлю в отдельную нить с обратной связью в системном трее, пока она еще загружается.

Это отношение "Мех, брось это в другую ветку, все будет хорошо", хорошо? В какой момент вы начинаете получать убывающую отдачу и / или даже сниженную производительность?

Ответы [ 10 ]

19 голосов
/ 14 июля 2009

Многопоточность делает много вещей, но я не думаю, что "упрощение" когда-либо является одним из них.

7 голосов
/ 14 июля 2009

Это отличный способ внести ошибки в код.

Правильно использовать несколько потоков - непросто. Это не должно быть предпринято новыми разработчиками.

4 голосов
/ 14 июля 2009

номер

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

Во-вторых, прирост производительности не происходит автоматически. Недавно в журнале MSDN вышла отличная статья . Существенные детали заключаются в том, что определенная операция занимала 46 секунд на десять итераций, закодированных как однопотоковая операция. Автор наивно распараллелил операцию (один поток на четыре ядра), и операция упала до 30 секунд за десять итераций. Звучит великолепно, пока вы не примете во внимание, что операция теперь потребляет на 300% больше вычислительной мощности, но эффективность выросла только на 34% Не стоит использовать всю доступную вычислительную мощность для такого усиления.

4 голосов
/ 14 июля 2009

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

2 голосов
/ 14 июля 2009

Читайте о Закон Амдала , лучше всего подытоженный "Ускорение программы, использующей несколько процессоров в параллельных вычислениях, ограничено временем, необходимым для последовательной доли программы."

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

2 голосов
/ 14 июля 2009

Это дает вам дополнительную работу по отладке условий гонки и обработке блокировок и проблем с сикронизацией.

Я бы не использовал это, если бы не было реальной необходимости.

1 голос
/ 16 июля 2009

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

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Да, потоки являются сложными и могут вызвать целый ряд проблем. Все остальные указали на это.

По опыту последовательность блокирования чтения / записи в сокет (для которой требуется отдельная thead) намного проще , чем неблокирующие. С помощью блокировки вызовов вы можете узнать состояние соединения, просто посмотрев, где вы находитесь в функции. Для неблокирующих вызовов вам понадобится множество переменных для записи состояния соединения, а также проверки и изменения их при каждом взаимодействии с соединением. При блокировке вызовов вы можете просто сказать «читать следующие X байтов» или «читать до тех пор, пока вы не найдете X», и он действительно сделает это (или потерпит неудачу). С неблокирующими вызовами вам приходится иметь дело с фрагментированными данными, которые обычно требуют хранения временных буферов и их заполнения по мере необходимости. Вы также проверяете, получили ли вы достаточно данных каждый раз, когда получаете немного больше. Кроме того, вы должны вести список открытых соединений и обрабатывать неожиданные закрытия для всех из них.

Это не намного проще, чем это:

void WorkerThreadMain(Connection connection) {
    Request request = ReadRequest(connection);
    if(!request) return;
    Reply reply = ProcessRequest(request);
    if(!connection.isOpen) return;
    SendReply(reply, connection);
    connection.close();
}

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

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

1 голос
/ 14 июля 2009

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

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

«Сборка» происходит в фоновом потоке; если бы это произошло в потоке пользовательского интерфейса, это сделало бы пользовательский опыт менее приятным - в конце концов, неинтересно, когда вы не можете сказать, можете ли вы щелкнуть виджет в приложении во время выполнения фоновой задачи ( кашель, Visual Studio). Не говоря уже о том, что VS не использует фоновые потоки, я просто говорю, что их пользовательский опыт может использовать некоторые улучшения. Но я отвлекся.

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

1 голос
/ 14 июля 2009

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

Прежде чем создавать другой поток для обработки, возможно, его можно проанализировать? Уменьшить, возможно, поместить в другой формат данных, который будет быстрее, и т. Д.

Как часто это меняется? Это то, что вы можете проанализировать один раз в начале дня и поместить переменные в общую память, чтобы последующие запуски вашей основной программы могли просто присоединяться и получать оттуда необходимые значения?

0 голосов
/ 14 июля 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...