Существуют ли какие-либо практические альтернативы потокам?

При чтении на SQLite я наткнулся на эту цитату в FAQ: "Нити злы. Избегайте их" .

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

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

Учитывая, что эти методы не полностью обойтись нитями (если я не понял, что говорит газета), я могу видеть два варианта: либо SQLite FAQ не означает буквально, что он говорит, либо существуют практические подходы, которые фактически избегают использование потоков в целом. Есть ли?


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

Ответ 1

Примечание. Этот ответ более точно не отражает то, что я думаю об этом предмете. Мне не нравится его чрезмерно драматичный, несколько неприятный тон. Кроме того, я не настолько уверен в том, что поиск доказуемо правильного программного обеспечения был настолько бесполезным, как я тогда думал. Я оставляю этот ответ, потому что он принят и проголосован, и редактировать его во что-то, что я сейчас верю, в значительной степени аннулирует его.


Наконец я добрался до газеты. С чего начать?

Автор поет старую песню, которая выглядит примерно так: "Если вы не можете доказать, что программа правильная, мы все обречены!" Это звучит лучше всего, когда громко кричали в сопровождении сверхмодулированных электрических гитар и быстрого удара барабаном. Академики начали петь эту песню, когда информатика была в области математики, в мире, где, если у вас нет доказательств, у вас ничего нет. Даже после того, как первый отдел компьютерной науки был откомандирован из отдела математики, они продолжали петь эту песню. Сегодня они поют эту песню, и никто не слушает. Зачем? Поскольку все мы заняты созданием полезных вещей, хорошие вещи из программного обеспечения, которые не могут быть доказаны правильно.

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

Если вы не были уверены, что автор живет в академическом мире мечты, вы можете быть уверены в этом, если он утверждает, что язык координации, который он предлагает в качестве альтернативы потокам, лучше всего выразить с помощью "визуального синтаксиса" (рисунок графики на экране). Я никогда не слышал этого предложения раньше, кроме каждого года моей карьеры. Язык, который можно манипулировать только графическим интерфейсом и не играет ни с одним из обычных инструментов программиста, не является улучшением. Далее автор цитирует UML как яркий пример визуального синтаксиса, который "обычно сочетается с С++ и Java". Обычно в каком мире?

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

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

Ответ 2

Заявление в SQLite FAQ, как я его прочитал, является просто комментарием о том, как непростая потоковая передача может быть непосвященной. Это мнение автора, и оно может быть действительным. Но, по-моему, вы никогда не должны использовать нитки, бросая ребенка с водой для ванны. Темы - это инструмент. Как и все инструменты, их можно использовать, и их можно злоупотреблять. Я могу прочитать его статью и убедиться, что нитки - это дьявол, но я успешно их использовал, не убивая котят.

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

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

Ответ 3

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

Ответ 4

Если вы действительно хотите жить без потоков, вы можете, если вы не вызываете никаких функций, которые могут блокировать. Это может быть невозможно.

Один из вариантов - реализовать задачи, которые вы сделали бы в потоках, как конечные автоматы. В основном задача делает то, что она может сделать немедленно, а затем переходит в следующее состояние, ожидая события, например, вход, поступающий в файл, или таймер, который отключается. X Windows, а также большинство инструментов GUI поддерживают этот стиль. Когда что-то происходит, они вызывают обратный вызов, который делает то, что ему нужно делать и возвращается. Для FSM обратный вызов проверяет, в каком состоянии находится задача, и какое событие должно определять, что делать немедленно, и каково будет следующее состояние.

Скажем, у вас есть приложение, которое должно принимать подключения сокетов, а для каждого соединения - синтаксические команды, выполнять некоторый код и возвращать результаты. Тогда задача будет состоять в том, что слушает сокет. Когда select() (или Gtk + или что-то другое) сообщает вам, что сокет имеет что-то для чтения, вы читаете его в буфер, а затем проверяете, достаточно ли буферизировать вход, чтобы что-то сделать. Если это так, вы переходите к состоянию "начать делать что-то", иначе вы остаетесь в состоянии "чтение строки". (То, что вы "делаете", может быть несколькими состояниями.) Когда закончите, ваша задача удаляет строку из буфера и возвращается к состоянию "чтение строки". Нет необходимости в потоках или предпосылке.

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

P.S. Другой способ получить потоки без использования потоков - это библиотека GNU Pth. Он не делает preemption, но это еще один вариант, если вы действительно не хотите иметь дело с потоками.

Ответ 5

Одна из тенденций, которые я заметил, по крайней мере в домене Cocoa, - это помощь из фреймворка. Apple приложила немало усилий, чтобы помочь разработчикам с относительно сложной концепцией параллельного программирования. Некоторые вещи, которые я видел:

  • Различная степень детализации резьбы. Cocoa поддерживает все: от потоков posix (низкий уровень) до объектно-ориентированной потоковой передачи с помощью NSLock и NSThread до высокоуровневого parellelism, такого как NSOperation. В зависимости от вашей задачи использование инструмента высокого уровня, такого как NSOperation, проще и выполняется.

  • Резьба за кулисами через API. Многие элементы интерфейса и анимации в Cocoa скрыты за API. Вы отвечаете за вызов метода API и предоставление асинхронного обратного вызова, которое выполняется при завершении вторичного потока (например, конец некоторой анимации).

  • OpenMP. Существуют такие инструменты, как openMP, которые позволяют вам предоставлять прагмы, которые описывают компилятор, что некоторая задача может быть безопасно решена. Например, итерация набора элементов независимым образом.

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

Ответ 6

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

Взгляните на механизмы, используемые в Clojure (например, agents, транзакционная память программного обеспечения).

Ответ 7

Программная транзакционная память (STM) является хорошей альтернативой concurrency. Он хорошо масштабируется с несколькими процессорами и не имеет большинства проблем обычных механизмов управления concurrency. Он реализован как часть Haskell. Это стоит попробовать. Хотя, я не знаю, как это применимо в контексте SQLite.

Ответ 8

Альтернативы потокам:

  • сопрограммы
  • goroutines
  • MapReduce
  • workerpool
  • яблочная грандиозная центральная отправка + лямбда
  • OpenCL
  • Эрл

(интересно отметить, что половина этих технологий была изобретена или популяризирована Google.)

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

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

Ответ 9

Threading - не единственная модель concurrency. Модель актеров (Erlang, Scala) является примером несколько иного подхода.

http://www.scala-lang.org/node/242

Ответ 10

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

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

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

Это действительно вопрос применения. Если ваша задача достаточно проста, чтобы вписаться в какой-то объект высокого уровня Task (зависит от вашей платформы разработки, ваш пробег может отличаться), то использование очереди задач - ваш лучший выбор. Мое правило большого пальца заключается в том, что если вы не можете найти классное имя для своего потока, то его задача не является достаточно важной, чтобы оправдать поток (вместо задачи, идущей в очереди операций).

Ответ 11

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

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

Ответ 12

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

Вещь - это не всегда жизнеспособно. В вашем случае 30-секундное время опроса на веб-сайт - можно ли его разделить на 60 штук 0,5 с, между которыми вы можете набивать вызовы в пользовательский интерфейс? Если нет, извините.

Нити не злые, их просто легко стрелять в ногу. Если выполнение Query A занимает 30 секунд, а затем выполнение Query B занимает еще 30 секунд, одновременное выполнение в потоках займет 120 с вместо 60 из-за накладных расходов на поток, борьбы за доступ к диску и различных узких мест.

Но если операция А состоит из 5 секунд активности и 55 секунд ожидания, смешанных случайным образом, а операция В занимает 60 секунд фактической работы, делая их в потоках займет, возможно, 70 с по сравнению с обычным 120, когда вы выполняете их последовательно.

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