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.
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.
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()