запуск нескольких потоков в Python одновременно - возможно ли это? - PullRequest
4 голосов
/ 09 сентября 2011

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

Я написал небольшой кусочек кода, который должен это сделать.

import thread
from urllib2 import Request, urlopen, URLError, HTTPError


def getPAGE(FetchAddress):
    attempts = 0
    while attempts < 2:
        req = Request(FetchAddress, None)
        try:
            response = urlopen(req, timeout = 8) #fetching the url
            print "fetched url %s" % FetchAddress
        except HTTPError, e:
            print 'The server didn\'t do the request.'
            print 'Error code: ', str(e.code) + "  address: " + FetchAddress
            time.sleep(4)
            attempts += 1
        except URLError, e:
            print 'Failed to reach the server.'
            print 'Reason: ', str(e.reason) + "  address: " + FetchAddress
            time.sleep(4)
            attempts += 1
        except Exception, e:
            print 'Something bad happened in gatPAGE.'
            print 'Reason: ', str(e.reason) + "  address: " + FetchAddress
            time.sleep(4)
            attempts += 1
        else:
            try:
                return response.read()
            except:
                "there was an error with response.read()"
                return None
    return None

url = ("http://www.domain.com",)

for i in range(1,50):
    thread.start_new_thread(getPAGE, url)

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

Я читал о GIL, есть ли способ обойти это без вызова кода C \ C ++? Я не могу понять, как возможно создание потоков с помощью GIL? Python в основном интерпретирует следующий поток, как только он заканчивает предыдущий?

Спасибо.

Ответы [ 5 ]

4 голосов
/ 09 сентября 2011

Как вы указали, GIL часто препятствует параллельной работе потоков Python.

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

В целом, однако, multiprocessing - более безопасная ставка, когда требуется истинный параллелизм.

1 голос
/ 09 сентября 2011

Вы можете использовать такой подход, чтобы создать все потоки, заставить их ждать объект условия, а затем заставить их начать извлекать URL " одновременно ":

#!/usr/bin/env python
import threading
import datetime
import urllib2

allgo = threading.Condition()

class ThreadClass(threading.Thread):
    def run(self):
        allgo.acquire()
        allgo.wait()
        allgo.release()
        print "%s at %s\n" % (self.getName(), datetime.datetime.now())
        url = urllib2.urlopen("http://www.ibm.com")

for i in range(50):
    t = ThreadClass()
    t.start()

allgo.acquire()
allgo.notify_all()
allgo.release()

Это немного приблизило бы вас к тому, чтобы все выборки происходили одновременно, НО :

  • Сетевые пакеты, покидающие ваш компьютер, будут последовательно проходить по проводу Ethernet, а нев то же время,
  • Даже если у вас есть 16+ ядер на вашей машине, некоторые маршрутизаторы, мосты, модемы или другое оборудование между вашей машиной и веб-хостом, вероятно, будут иметь меньше ядер и могут сериализовать вашизапросов,
  • Веб-сервер, с которого вы загружаете данные, будет использовать вызов accept() для ответа на ваш запрос.Для правильного поведения это реализовано с использованием глобальной блокировки сервера, чтобы гарантировать, что только один процесс / поток сервера отвечает на ваш запрос.Даже если некоторые из ваших запросов поступят на сервер одновременно , это приведет к некоторой сериализации.

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

1 голос
/ 09 сентября 2011

Я читал о GIL, есть ли способ обойти это без вызова кода C \ C ++?

Не совсем. Функции, вызываемые через ctypes, освобождают GIL на время этих вызовов. Функции, которые выполняют блокировку ввода-вывода, тоже будут освобождены. Существуют и другие подобные ситуации, но они всегда включают код вне основного цикла интерпретатора Python. Вы не можете отпустить GIL в своем коде Python.

0 голосов
/ 06 октября 2012

Если вы запустите свой код с помощью Jython или IronPython (и, возможно, PyPy в будущем), он будет работать параллельно

0 голосов
/ 09 сентября 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...