Python. Многопоточность

Недавно была такая задача:
был простенький скрипт стягивал в вебе странички. очень нужные странички, как оказалось. И, как обычно на вчера, нужно было сделать этот скрипт сделать многопоточным, чтобы странички скачивались быстрее. Порыл в нете на эту тему — есть довольно много интересных оберток для стандартного модуля threading , но увы, как правило они были или кривые или вообще не работали. Поэтому почерпнув идей из кривых оберток решил написать свой класс для быстрого превращения обычного скрипта в многопоточный.

Сформулировал для себя задачу так:
1. Простое подключение к уже существующему скрипту.
2. Модель — thread pool. Т.е. сначала поднимается заданное кол-во потоков, а потом добавляются задачи в общую очередь. Потоки разбирают себе по задаче, остальные задачи ждут пока не освободится поток. Связано с тем что рождение потока — довольно долгая операция, дешевле один раз поднять и использовать повторно.
3. Возможность ожидания задач. Т.е. если в очереди нет задач, ждать пока они появятся
4. Возможность безопасного останова. Т.е. закончили обработку задач в потоках — и аккуратно вышли.
5. Возможность «плавного» запуска потоков. Связано с тем что если например нужно скачать веб страницы с одного сервера в 150-200 потоков — удаленный сервер часто будет давать таймауты. Что бы этого не было — нужно использовать запуск потоков с задержкой

ну и собственно код

6 комментариев

  1. Насчёт плавного пуска. Не понимаю, как это влияет, если сервер таки всё равно будет часто таймаутить при большом числе потоков?

  2. представь, ты одновременно пускаешь на сервер 200 запросов. У сервера есть лимит одновременных коннектов. Т.е. сервер обрабатывает допустим 50 запросов одновременно, 150 ставит в очередь. Так же есть лимит ожидания обработки запроса. Допустим, до этого таймаута успело обработаться 100 запросов из очереди. Итого, для 50 запросов будет таймаут.
    А если пускать плавно, то можно настроить потоки так что одновременно 200 запросов никогда не пошлются, их число в один момент времени будет гораздо меньше. Мало того, веб сервер сможет успеть поднять дополнительные обработчики запросов.

  3. было бы здорово если еще реализовать подключение через proxy. Перехожу с php на python только из за многопоточности)

  4. Замечательная вещь, спасибо. Как раз то что нужно. Вот только не могу сообразить как определить когда все потоки закончат работать?

  5. поток умирает когда целевая функция (в данном случае Worker.run) дойдет до конструкции return. И даже если в самой функции не стоит явно return, он все равно вызывается в конце функции. А отслеживание «умирания» всех потоков берет на себя worker.join функция
    Она описана в threading.Thread модуле

    Насчет прокси — тут все-таки просто реализация worker’a, а что он будет делать — уже зависит от конкретной задачи. Сделать коннект через прокси задача довольно простая

  6. МОжет кому пригодиться.
    сделал так:
    #ждем пока опустеет очередь
    while pool.queue.queue:
    pass
    #time.sleep(5)
    #и только потом ждем пока живы процессы
    z = Pool.workers
    while z:
    #time.sleep(3)
    for i,p in enumerate(z):
    if not p.isAlive():
    del(z[i])

Leave a Reply

Ваш e-mail не будет опубликован. Обязательные поля помечены *