2010-01-15 2 views
11

При чтении на SQLite, я наткнулся на эту цитату в FAQ: "Threads are evil. Avoid them."Есть ли практические альтернативы потокам?

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

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

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


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

+1

Не собираюсь добавлять это в качестве ответа, потому что я не чувствую, как его обманывают, но: неблокирующий IO, совместная многозадачность, многопроцессорность + IPC ... и, возможно, потоки. Правильное соединение зависит от отдельного приложения. – hobbs

+0

Для справки, SQLite часто задаваемые вопросы «Проблема с потоками». Из абстракции: «Они отбрасывают наиболее существенные и привлекательные свойства последовательных вычислений: понятность, предсказуемость и детерминизм. Нити, как модель вычислений, дико недетерминированы, а работа программиста превращается в обрезку этого недетерминизма. .. Я спорю о разработке параллельных языков координации на основе звуковых, композиционных формализмов. Я считаю, что такие языки будут давать гораздо более надежные и более параллельные программы ». –

ответ

6

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


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

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

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

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

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

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

+0

«Даже без потоков, только самые тривиальные программы могут быть доказаны правильно», да, только самые тривиальные, как ядра ОС и компиляторы. –

+0

«Даже без потоков, только самые тривиальные программы могут быть доказаны правильно». Это просто эмпирически неправильно. –

+0

@KevinKeith Для частичной корректности, я думаю, вы правы. Я не знал о частичной правильности, когда писал это; еще одна причина, что это отвратительный ответ, на который никто не должен обратить внимание. –

5

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

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

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

+3

-1. Я не думаю, что это разумная интерпретация заявления «Нити злы», особенно когда это ссылка на бумагу, которая аргументирует в значительной степени, что потоки - плохая идея. (Из абстракции: «Они отбрасывают самые важные и привлекательные свойства последовательных вычислений: понятность, предсказуемость и детерминизм».) –

+0

Un-downvoted! Должен сказать, что я все еще не согласен с тобой. Все общие темы похожи на C-указатели: полезные и опасные. Большинство людей, которые их используют, утверждают, что их можно использовать безопасно, но мой опыт настоятельно предлагает принимать такие претензии с солью. Этот документ стоит прочитать. –

2

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

2

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

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

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

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

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

+0

Как использовать Pth не используя потоки? Они могут не быть потоками ОС, но вы по-прежнему используете одно и то же адресное пространство, которое позволяет избежать потоков. +1 для FSM. – Jacob

+0

Есть несколько причин, почему кто-то сказал бы: «Не используйте потоки». Иногда Pth является приемлемой альтернативой, а иногда и нет. –

2

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

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

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

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

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

+1

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

2

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

Взгляните на механизмы, используемые в Clojure (например, agents, software transactional memory).

1

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

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

1

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

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

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

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

2

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

+0

Конечно, но STM не является способом для _avoid_ threads, а скорее для безопасного доступа к общему состоянию, когда вы _do_ используете потоки. –

+1

romkyns: если связанная бумага представляет собой какое-либо указание, возражения пользователей SQLite на потоки связаны с огромными проблемами с потоками * в качестве модели программирования * - особенно для Java-стиля, разделяемых -всех потоков: полного отсутствия изоляции по умолчанию, склонность к скрытым ошибкам, и так далее. У STM нет этих проблем. (У него другие проблемы.) –

2

Альтернативы резьб:

  • сопрограммы
  • goroutines
  • MapReduce
  • workerpool
  • Grand Central Dispatch яблока + лямбды
  • OpenCL
  • Erlang

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

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

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

+0

Если бы я мог принять два ответа, это было бы одним из двух. Не могу сказать, что один лучше, чем другой, я думаю, что они оба хороши и дополняют друг друга. Другой ответ «выиграл», потому что у него меньше голосов и, следовательно, слишком мало видимости ИМО. –

+0

Точно так же вы не будете использовать Javascript для написания веб-приложения ... Боже, я надеюсь, что мое предыдущее предложение скоро станет полностью правдой. –

1

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

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

+0

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

+0

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

0

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

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

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

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

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

+0

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