Использовать случаи для ithreads (потоки интерпретатора) в Perl и обоснование использования или не использования их?
Если вы хотите узнать, как использовать потоки интерпретатора Perl, там есть хорошая документация в perlthrtut
(руководство по темам) и threads
pragma manpage. Это определенно достаточно хорошо для написания простых сценариев.
Тем не менее, я нашел небольшое руководство в Интернете о том, почему и зачем разумно использовать потоки интерпретатора Perl. На самом деле, о них мало что говорят, и если люди говорят о них, довольно часто препятствовать людям использовать их.
Эти потоки, доступные, когда perl -V:useithreads
равно useithreads='define';
и развязаны use threads
, также называются ithreads и, возможно, более подходящими, так как они сильно отличаются от потоков, предлагаемых операционными системами Linux или Windows, или Java VM в том, что ничто не разделяется по умолчанию, и вместо этого копируется много данных, а не только стек потоков, что значительно увеличивает размер процесса. (Чтобы увидеть эффект, загрузите несколько модулей в тестовый script, затем создайте потоки в цикле, останавливающихся для нажатия клавиш каждый раз, и наблюдайте увеличение памяти в диспетчере задач или top
.)
[...] каждый раз, когда вы запускаете поток, все структуры данных копируются в новый поток. И когда я говорю все, я имею в виду все. Это, например, включает пакеты, глобальные переменные, лексики в области. Все!
- Что вам нужно знать перед программированием Perl ithreads (Perlmonks 2003)
Когда вы изучаете предмет Perl ithreads, вы увидите, что люди не поощряют вас использовать их ( "крайне плохая идея" , "принципиально ошибочная" или "никогда не использовать ithreads для чего-либо" ).
В учебнике по потокам Perl подчеркивается, что "Perl Threads Different" , но он не очень беспокоится, чтобы объяснить, как они отличаются и что это означает для пользователя.
Полезным, но очень кратким объяснением того, что на самом деле существует ithreads, является из Coro
man-страницы под заголовком WINDOWS PROCESS EMULATION. Автор этого модуля (Coro - единственные реальные потоки в perl) также не рекомендует использовать потоки интерпретатора Perl.
Где-то я читал, что компиляция perl с включенными потоками приведет к значительно более медленному интерпретатору.
На странице Perlmonks с 2003 года (Что вам нужно знать перед программированием Perl ithreads), в котором автор спрашивает: "Теперь вы может удивиться, почему Perl ithreads не использовал fork()? Разве это не имело бы больше смысла?" Это, похоже, написано автором прагмы forks
. Не уверен, что информация, указанная на этой странице, сохраняется в 2012 году для новых Perls.
Вот некоторые рекомендации по использованию потоков в Perl, которые я перегонял из своих чтений (возможно, ошибочно):
До сих пор мое исследование. Теперь, спасибо за любой свет, который вы можете пролить на эту проблему потоков в Perl. Каковы некоторые разумные варианты использования для ithreads в Perl? В чем причина их использования или не использования?
Ответы
Ответ 1
Короткий ответ: они довольно тяжелые (вы не можете запустить их более дешево), и они демонстрируют неожиданное поведение (несколько смягченное последними модулями CPAN).
Вы можете безопасно использовать Perl ithreads, рассматривая их как независимых Актеров.
- Создать поток:: Queue:: Any для "work".
- Запустить несколько ithreads и "result" Очереди, передающие им ( "work" + собственный "результат" ) Очереди по закрытию.
- Загрузка (требуется) всего остального кода, требуемого вашим приложением (не до потоков!)
- При необходимости добавьте работу для потоков в очередь.
В "рабочих" ithreads:
- Ввести любой общий код (для любой работы)
- Блокировка - удаление очереди из очереди
- Спросить - загрузить любые другие зависимости, необходимые для этой части работы.
- Выполняйте работу.
- Передайте результат обратно в основной поток через очередь результатов.
- Вернуться к 2.
Если некоторые "рабочие" потоки начинают становиться немного мутными, и вам нужно ограничить потоки "работника" некоторым числом, а затем запускать новые на своем месте, а затем сначала создать поток "пусковой установки", задачей которого является запускать "рабочие" потоки и подключать их к основному потоку.
Каковы основные проблемы с Perl ithreads?
Они немного неудобны для "общих" данных, так как вам нужно объяснять общий доступ (не большая проблема).
Вам нужно следить за поведением объектов с помощью методов DESTROY, поскольку они выходят из области видимости в каком-то потоке (если они еще нужны в другом!)
Большая: данные/переменные, которые явно не разделены, - это CLONED в новые потоки. Это удар производительности и, возможно, совсем не то, что вы намеревались. Работа вокруг заключается в том, чтобы запустить ithreads из довольно "первозданного" состояния (не так много загруженных модулей).
IIRC, в пространстве Threads:: существуют модули, которые помогают с явным определением зависимостей и/или очисткой клонированных данных для новых потоков.
Кроме того, IIRC, есть немного другая модель, использующая ithreads, называемые потоками "квартиры", реализованные Thread:: Appartment, которая имеет другой шаблон использования и другой набор компромиссов.
Результат:
Не используйте их, если вы не знаете, что делаете: -)
Fork может быть более эффективным в Unix, но история IPC намного проще для ithreads. (Это могло быть смягчено модулями CPAN с тех пор, как я в последний раз смотрел: -)
Они еще лучше, чем потоки Python.
В Perl 6 может быть что-то гораздо лучше.
Ответ 2
Я несколько раз использовал perl-потоки. Они наиболее полезны для запуска некоторого процесса и продолжения с чем-то еще. У меня нет большого опыта в теории того, как они работают под капотом, но у меня есть много практического опыта в кодировании с ними.
Например, у меня есть поток сервера, который прослушивает входящие сетевые подключения и выдает ответ о состоянии, когда кто-то спрашивает об этом. Я создаю этот поток, затем перемещаюсь и создаю еще один поток, который контролирует систему, проверяет пять элементов, спящий несколько секунд и снова зацикливается. Для сбора данных монитора может потребоваться 3-4 секунды, затем он будет завален в общую переменную, и поток сервера может прочитать, когда это необходимо, и немедленно вернуть последний известный результат кому бы то ни было. Поток монитора, когда он обнаруживает, что элемент находится в плохом состоянии, запускает отдельный поток для восстановления этого элемента. Затем он переходит, проверяя другие предметы, пока плохой ремонтируется, и отбрасывает другие потоки для других плохих предметов или присоединяется к завершенным потокам ремонта. Основная программа все время зацикливается каждые несколько секунд, следя за тем, чтобы потоки монитора и сервера не соединялись/не выполнялись. Все это может быть написано как куча отдельных программ, использующих другую форму IPC, но потоки perl упрощают ее.
Другое место, где я их использовал, - это фрактальный генератор. Я бы разделил части изображения с помощью некоторого алгоритма, а затем запустил столько потоков, сколько у меня есть процессоры для выполнения этой работы. Каждый из них сводил их результаты в один объект GD, который не вызывал проблем, потому что каждый из них работал с разными частями массива, а затем, когда это было сделано, я выписал изображение GD. Это было мое введение в использование потоков perl и было хорошим введением, но затем я переписал его на C, и это было на два порядка быстрее:-). Затем я переписал версию Perl с потоком, чтобы использовать Inline:: C, и это было всего на 20% медленнее, чем чистая версия C. Тем не менее, в большинстве случаев, когда вы хотите использовать потоки из-за интенсивного использования процессора, вы, вероятно, захотите просто выбрать другой язык.
Как упоминалось другими, fork и threads действительно перекрываются для многих целей. Coro, однако, на самом деле не позволяет использовать многопроцессорную или параллельную обработку, например, fork и thread, вы будете видеть только ваш процесс, используя 100%. Я слишком упрощаю это, но я думаю, что самый простой способ описать Coro - это планировщик для ваших подпрограмм. Если у вас есть подпрограмма, которая блокирует вас, вы можете прыгать в другую и делать что-то еще, пока вы ждете, например, у вас есть приложение, которое вычисляет результаты и записывает их в файл. Один блок может вычислять результаты и вставлять их в канал. Когда он заканчивается, другой блок начинает записывать их на диск. Пока этот блок ждет диск, другой блок может снова начать вычислять результаты, если он получает больше работы. По общему признанию, я не много сделал с Коро; это звучит как хороший способ ускорить некоторые вещи, но меня немного отпугивает, не имея возможности сделать сразу две вещи.
Мои личные предпочтения, если я хочу многопроцессорствовать, - использовать fork, если я делаю много мелких или коротких вещей, потоков для нескольких больших или долгоживущих вещей.