Как сделать неблокирующую выборку URL в Python - PullRequest
2 голосов
/ 11 июня 2009

Я пишу приложение с графическим интерфейсом на Pyglet , которое должно отображать от десятков до сотен миниатюр из Интернета. Прямо сейчас я использую urllib.urlretrieve , чтобы захватить их, но это блокирует каждый раз, пока они не завершены, и только захватывает один по одному за раз.

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

Я не знаю много о потоках, но похоже, что модуль threading может помочь? Или, возможно, есть какой-то простой способ, который я упустил.

Ответы [ 6 ]

3 голосов
/ 12 июня 2009

Вы, вероятно, получите выгоду от threading или multiprocessing модулей. На самом деле вам не нужно создавать все эти Thread классы самостоятельно, есть более простой метод, использующий Pool.map:

from multiprocessing import Pool

def fetch_url(url):
    # Fetch the URL contents and save it anywhere you need and
    # return something meaningful (like filename or error code),
    # if you wish.
    ...

pool = Pool(processes=4)
result = pool.map(f, image_url_list)
2 голосов
/ 12 июня 2009

Вот пример того, как использовать потоки. Просто замените имя класса своим собственным, а функцию run - своим. Обратите внимание, что многопоточность отлично подходит для приложений с ограниченным вводом-выводом, таких как ваши, и может реально ускорить ее. Использование потоков Pythong строго для вычислений в стандартном Python не помогает, потому что только один поток может вычислять одновременно.

import threading, time
class Ping(threading.Thread):
    def __init__(self, multiple):
        threading.Thread.__init__(self)
        self.multiple = multiple
    def run(self):
        #sleeps 3 seconds then prints 'pong' x times
        time.sleep(3)
        printString = 'pong' * self.multiple

pingInstance = Ping(3)
pingInstance.start() #your run function will be called with the start function
print "pingInstance is alive? : %d" % pingInstance.isAlive() #will return True, or 1
print "Number of threads alive: %d" % threading.activeCount()
#main thread + class instance
time.sleep(3.5)
print "Number of threads alive: %d" % threading.activeCount()
print "pingInstance is alive?: %d" % pingInstance.isAlive()
#isAlive returns false when your thread reaches the end of it's run function.
#only main thread now
2 голосов
/ 11 июня 2009

Как вы правильно указали, вы можете создать несколько потоков, каждый из которых отвечает за выполнение операций urlretrieve. Это позволяет основному потоку продолжать непрерывно.

Вот учебник по многопоточности в python: http://heather.cs.ucdavis.edu/~matloff/Python/PyThreads.pdf

2 голосов
/ 11 июня 2009

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

1 голос
/ 12 июня 2009

У вас есть следующие варианты:

  • Темы: проще всего, но плохо масштабируются
  • Скрученный: средней сложности, хорошо масштабируется, но разделяет ЦП благодаря GIL и является однопоточным.
  • Многопроцессорная обработка: сложная. Хорошо масштабируется, если вы знаете, как написать свой собственный цикл обработки событий.

Я рекомендую использовать только потоки, если вам не нужен сборщик промышленного масштаба.

0 голосов
/ 12 июня 2009

Вам нужно либо использовать потоки, либо асинхронную сетевую библиотеку, например Twisted . Я подозреваю, что использование потоков может быть проще в вашем конкретном случае.

...