Jak używać gwintowania w Pythonie?

Próbuję zrozumieć gwintowanie w Pythonie. I've spojrzał na dokumentację i przykłady, ale szczerze mówiąc, wiele przykładów jest zbyt wyrafinowanych i mam problemy z ich zrozumieniem.

W jaki sposób wyraźnie pokazujesz, że zadania są dzielone dla wielowątkowości?

Oto prosty przykład: musisz wypróbować kilka alternatywnych adresów URL i zwrócić zawartość pierwszego, który odpowie.

import Queue
import threading
import urllib2

# called by each thread
def get_url(q, url):
    q.put(urllib2.urlopen(url).read())

theurls = ["http://google.com", "http://yahoo.com"]

q = Queue.Queue()

for u in theurls:
    t = threading.Thread(target=get_url, args = (q,u))
    t.daemon = True
    t.start()

s = q.get()
print s

Jest to przypadek, w którym wątkowanie jest używane jako prosta optymalizacja: każdy podwątek czeka na URL do rozwiązania i odpowiedzi, aby umieścić jego zawartość na kolejce; każdy wątek jest demonem (won't keep the process up if main thread ends -- that's more common than not); główny wątek uruchamia wszystkie podwątki, wykonuje get na kolejce, aby poczekać, aż jeden z nich wykona put, następnie emituje wyniki i kończy (co zdejmuje wszelkie podwątki, które mogą być nadal uruchomione, ponieważ'są to wątki demonów).

Prawidłowe użycie wątków w Pythonie jest nieodmiennie związane z operacjami wejścia/wyjścia (ponieważ CPython nie używa wielu rdzeni do wykonywania zadań związanych z procesorem, jedynym powodem użycia wątków jest nieblokowanie procesu podczas oczekiwania na jakieś wejście/wyjście). Kolejki są prawie niezmiennie najlepszym sposobem na przekazanie pracy do wątków i/lub zebranie jej wyników, a przy okazji są one z natury bezpieczne dla wątków, więc nie musisz się martwić o zamki, warunki, zdarzenia, semafory i inne koncepcje koordynacji/komunikacji między wątkami.

Komentarze (14)

UWAGA: Do rzeczywistej paralelizacji w Pythonie należy użyć modułu multiprocessing, aby rozwidlić wiele procesów, które wykonują się równolegle (ze względu na globalną blokadę interpretera wątki Pythona zapewniają przeplatanie, ale w rzeczywistości są wykonywane szeregowo, a nie równolegle, i są przydatne tylko przy przeplataniu operacji wejścia/wyjścia).

Jeśli jednak szukasz tylko przeplotu (lub wykonujesz operacje wejścia/wyjścia, które mogą być sparalelizowane pomimo globalnej blokady interpretera), wtedy threading moduł jest miejscem, od którego powinieneś zacząć. Jako naprawdę prosty przykład, rozważmy problem sumowania dużego zakresu przez równoległe sumowanie podprzedziałów:

import threading

class SummingThread(threading.Thread):
     def __init__(self,low,high):
         super(SummingThread, self).__init__()
         self.low=low
         self.high=high
         self.total=0

     def run(self):
         for i in range(self.low,self.high):
             self.total+=i

thread1 = SummingThread(0,500000)
thread2 = SummingThread(500000,1000000)
thread1.start() # This actually causes the thread to run
thread2.start()
thread1.join()  # This waits until the thread has completed
thread2.join()  
# At this point, both threads have completed
result = thread1.total + thread2.total
print result

Zauważ, że powyższy przykład jest bardzo głupi, ponieważ nie robi absolutnie żadnego I/O i będzie wykonywany seryjnie, aczkolwiek z przeplotem (z dodanym narzutem przełączania kontekstu) w CPython z powodu globalnej blokady interpretera.

Komentarze (8)

Jak wspomnieli inni, CPython może używać wątków tylko dla IO waits z powodu GIL. Jeśli chcesz skorzystać z wielu rdzeni dla zadań związanych z procesorem, użyj multiprocessing:

from multiprocessing import Process

def f(name):
    print 'hello', name

if __name__ == '__main__':
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()
Komentarze (5)