Как стать автором
Обновить

Комментарии 39

Перенесите в блог по питону, когда кармы наберете (а вы наберете :) ). Хороший материал.
Обязательно перенесу. Спасибо! =)
топик перенес в блог «Язык программирования Python»!
Спасибо за карму! =)
Как раз искал. Спасибо за статью.

P.S.: Заметил странность — слово «многопоточность» в программировании встречается столько же, сколько «позиционирование» в маркетинге.
Насчет полезности мне трудно уже судить, поэтом побрюзжу:

0. Зачем использовать print для вывода отладочной информации, когда есть простой и удобный logging;
1. Threading это всеже нити. Для работы с многопоточностью есть multiprocessing;
2. Для практического применения прокси на тредах не годится (или только при мизерных нагрузках).
0) print работает исключительно как индикатор
1) Под Threading подразумевал именно нити (по поводу путаницы с терминологией я предупреждал)
2) Что именно подразумевается под практическим применением?
>>> Что именно подразумевается под практическим применением?

Много запросов. Для домашнего пользования зачастую будет проще настроить тотже squid.
1) Threads — это потоки, в некоторых переводах «нити». Мне много больше нравятся потоки, так как они хоть что-то означают. Путаницы с I\O потоками (те что streams) тут совсем нету. Multiprocessting — это чуть другое. Об этом можно почитать в POSIX модели процессов и потоков.
А вообще можно переводить Threads как угодно, но учтите, что очень много программистов никогда и не слышали о нитях.
З.Ы. А как перевести multithreading?
А что вы скажете про Stackless?
Я вам скажу за Stackless — в стаклес реализована концепция сопрограмм, а это несколько другое.
ааа, спасибо огромное! как раз задумался над таким парсингом. завтра перепишу парочку своих скриптов
1. В 2.5 питоне тоже есть with statements: from __future__ import with_statement
2. Не хватает дисклеймера со словами «естественно, я понимаю, что писать многопоточный парсер google.com — затея не из лучших»

Кстати, работу с локами тоже проще завернуть в with ;)
Мне одному кажется что вы переборщили с комментариями? По мне так лучше пусть было бы
красиво написанное
и откомментированное в несколько раз менее объемно.
да, Вы правы, комментариев много, но ни по одной строчке кода не возникнет никаких вопросов (добивался именно этого) =)
Надо было добавить ещё код с молдавскими комментариями ))
если Вы обратите внимание на маааленькую ссылочку в самом низу статьи и перейдёте туда, то увидите что там принято писать по-украински =)
Есть вопрос. Не уверен, что автор сможет дать ответ, но может кто-то:
Правильно ли я понял, RLock & Lock это аналоги recursive mutex & non-recursive mutex из POSIX thread model?
судя по комментам в коде это именно оно и есть.
зачем писать global? в вашем случае это вообще ни к чему
global используется для того, чтобы не возникло никаких вопросов по поводу того что откуда берется =)
да лучше бы возникли вопросы у тех, кто не знает что и как, это же не урок по основам питона
Если претендуете на академичность кода, то вот это пятна на нём:
try:
target_link = queue.get_nowait()
except Exception, error:
return

for _ in xrange(THREADS_COUNT):
В первом абзаце понятно — антипаттерн «Подгузник».

А во втором что?
его продолжение
Да, вы правы. У вас 100% говонокод.
Что будет с вашими потоками, если отвалиться сеть, или гугл начнет отдавать 404? Они повиснут, т.к очередь никогда не отчиститься.
Что будет, если кончится место на диске? Потоки зависнут, т.к после взятия лока вылетит исключение на open(), и лок уже не будет освобожден.

То, что код работает и в нем много комментариев — не причина демонстрировать его, называя статью «основы работы с потоками».

Хороший пример работы с потоками — github.com/svetlyak40wt/couchdb-performance-tests/blob/master/couchdb_bulk_perf.py
Спасибо за ссылку, давно я искал чтоб было наглядно с очередью, свою писал. жесть.
о да, а если выключится компьютер, планета остановится, или того хуже — у меня закончится кофе! особенно интересно насчет окончания места на диске — вам не кажется, что в таком случае то уже и смысла нет в его дальнейшей работе как таковой (не, это не уменьшает моей быдлокодерской ошибки, но это факт). П.С. приведенный вами пример — это пример того, как я делать не буду.
особенно интересно насчет окончания места на диске — вам не кажется, что в таком случае то уже и смысла нет в его дальнейшей работе как таковой

конечно, в случае ошибки лучше наглухо зависнуть никому не сказать о причине

о да, а если выключится компьютер, планета остановится, или того хуже — у меня закончится кофе!

тогда и пишите во введении, что у вас пост про код с кучей концептуальных!!! багов (дедлок, это как раз концептуальный баг в многопоточной программе) а не про «понимания основных принципов работы многопоточных приложений»
Это же жесть:
return «ERROR»
if parsed_data != «ERROR»:

___
Чем Вам BOOL помешал? или брослии бы исключение

Файлы и потоки это жесть номер два, я понимаю что это учебка. но изначально нужно закладывать правлиьное видение потоков, если у вас 1000 из 20000 и более потоянно.

по опыту скажу, что лучше «get_and_parse_page» делать не в потоке, у вас просто 100 проц. вермени будет сжираться от регекспа, т.е. я хотел сказать, что страницы нужно напарсить в потоках, тут все правльино, и дальше сделать генерарор, который будет в цикле, так вот по выходу из генератора все что накачали ( ну там 100 страниц допустим), уже делать регексп

вы соверенно не обрабатываете такие вещи как недоступность прокси, 403 от гугла и т.д. при такой схеме вас на 3-4 поток забанят ( пауза в 1 сек совершено бесполезна!) )( понимаю, что это пример, но вы хотябы в коментах про это отписали бы )
Мне вот отмазка, что это мол пример, не канает. Зачем мне пример, если там все через задницу? Даже я, и то вижу, что это плохой код. А пафос, с которым статья преподнесена, усугубляет во сто крат.
мне всегда нравились фразы типа: бла-бла, я здесь сделаю такую штуку, но вы такую штуку не делайте, я этого не рекомендую! :) зачем самому делать то что не рекомендуете?

а за топик спасибо, как раз сегодня хотел посмотреть что-то по работе с потоками
Если вы про мой ответ, то я не рекомендовал делать в потоках регехп, так как он бесполезен в потоках, только нагружает проц, да еще если помножить на потоки, то получаеться как раз медленней, чем в прямой цикл поставить и обработать
А если вообще нафиг послать потоки в чтении страниц и просто асинхронно через epoll их тащить? Будет же вообще комильфо…
пример бы дали.
Попробую написать на неделе что-нибудь такое.

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

Если интересно, из питона интерфейс к асинхронным сокетам можно получить через модуль select.
не я об этом говорил:
global queue
#Здесь и далее я буду обьявлять функции из глобального пространства
#имен в локальном для лучшей читабельности кода, хотя в написании
#софта такое делать строго не рекомендую (!)
Не феншуйно читать комментарии, написанные под строкой, к которой они относятся. Обычно они сверху или в той же строке (pep8) и к этому все привыкли.

Как-то не очень с исключениями. Во-первых, except и сразу return может стоить вам многих часов, разбираясь почему «оно» не работает как как надо. Во-вторых, при работе с потоками (thread'ам), важно помнить две вещи — поток должен сам перехватывать свои собственные исключения и acquire/release должны быть обернуты try/finally или lock подсовывать with'у. Если write_to_file бросит исключение при открытии файла, будет и дедлок и необработанное иключение. Кстати print для иключения далеко не самый лучший индикатор, logging.exception поинформативней будет.

И еще, ENCODING то используется, то не используется.
Добавлю свои 5 копеек: некто не запрещает разделить задачу еще больше — сделать 3 очереди и 3 типа потоков (скачка, парсинг, сохранение). Это позволит начать обработку входных данных не дожидаясь окончательной их загрузки (если потоки запустить до начала загрузки данных в очередь) и сильнее разнести части кода (например для увеличения кол-ва разработчиков). Дальше можно нитки демонизировать, что позволит оставить их болтаться до тех пор пока не закончится основной поток. Забирать данные в этом случае можно и проще и надежнее:
в нитке остаётся только бесконечно крутить:
while True:
# забираем данные из очереди дожидаясь пока они там будут
data = queueN.get()
# делаем свое грязно дело
do_some_work(data)
# помечаем сделанную работу в очереди
queue.task_done()

в основной поток будет выглядеть примерно вот так:
queue1 = Queue()
queue2 = Queue()
queue3 = Queue()

# тип 1
for _ in range(NUMBER):
thread_ = threading.Thread(target=worker)
thread_.setDaemon(True) # для питона 2.5 (в 2.6+ есть обратносовместимый метод)
thread_.start()
# тип 2
for _ in range(NUMBER):
thread_ = threading.Thread(target=worker2)
thread_.setDaemon(True)
thread_.start()

# тип 3
for _ in range(NUMBER):
thread_ = threading.Thread(target=worker3)
thread_.setDaemon(True)
thread_.start()

# тут запихиваем в очереди данные, и по мере того как очередь будет наполнятся она начнет обрабатываться

# ждем когда закончатся все очереди
queue1.join()
queue2.join()
queue3.join()

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

Публикации

Истории