Hvordan bruke threading i Python?

Jeg prøver å forstå threading i Python. Jeg har sett på dokumentasjonen og eksemplene, men ærlig talt er mange eksempler altfor sofistikerte, og jeg har problemer med å forstå dem.

Hvordan viser du tydelig at oppgaver blir delt for multi-threading?

Her er et enkelt eksempel: Du må prøve noen alternative nettadresser og returnere innholdet i den første som svarer.

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

Dette er et tilfelle der tråding brukes som en enkel optimalisering: Hver undertråd venter på at en URL skal løse og svare, for å sette innholdet i køen; hver tråd er en demon (vil ikke holde prosessen oppe hvis hovedtråden slutter - det er mer vanlig enn ikke); hovedtråden starter alle undertråder, gjør en get på køen for å vente til en av dem har gjort en put, og sender deretter resultatene og avsluttes (som tar ned eventuelle undertråder som fremdeles kjører, siden de er demon-tråder).

Riktig bruk av tråder i Python er alltid koblet til I / O-operasjoner (siden CPython ikke bruker flere kjerner for å kjøre CPU-bundne oppgaver uansett, er den eneste grunnen til at threading ikke blokkerer prosessen mens det er ventetid på noen I / O). Køer er nesten alltid den beste måten å drive ut arbeid til tråder og / eller samle arbeidets resultater, forresten, og de er iboende trådsikre, slik at de sparer deg for å bekymre deg for låser, forhold, hendelser, semaforer og andre inter-trådkoordinering / kommunikasjonskonsepter.

Kommentarer (14)

MERK: For faktisk parallellisering i Python, bør du bruke modulen multiprocessing for å gaffel flere prosesser som kjøres parallelt (på grunn av den globale tolkerlåsen gir Python-tråder sammenfletting, men utføres faktisk serielt, ikke parallelt, og er bare nyttige når du fletter sammen I / O-operasjoner).

Imidlertid, hvis du bare leter etter sammenfletting (eller gjør I / O-operasjoner som kan parallelliseres til tross for den globale tolkelåsen), er threading modulen stedet å starte. Som et veldig enkelt eksempel, la oss vurdere problemet med å summere et stort område ved å summere underområder parallelt:

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

Merk at det ovennevnte er et veldig dumt eksempel, da det absolutt ikke gjør noen I / O og vil bli utført serielt om enn sammenflettet (med den ekstra overhead av kontekstbytte) i CPython på grunn av den globale tolkelåsen.

Kommentarer (8)

Som andre nevnte, kan CPython bare bruke tråder for I\O venter på grunn av GIL. Hvis du vil dra nytte av flere kjerner for CPU-bundne oppgaver, bruk multiprocessing:

from multiprocessing import Process

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

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