Es mēģinu izprast Python pavedienu veidošanu. Esmu apskatījis dokumentāciju un piemērus, bet, atklāti sakot, daudzi piemēri ir pārāk sarežģīti, un man ir grūtības tos saprast.
Kā jūs skaidri parādāt uzdevumu sadalīšanu vairāku pavedienu režīmā?
Lūk, vienkāršs piemērs: jums ir jāizmēģina vairāki alternatīvi URL adresāti un jāatgriež pirmā, kas atbildēs, saturs.
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
Šis ir gadījums, kad vītņošana tiek izmantota kā vienkārša optimizācija: Katrs apakšvēstījums gaida, kad URL tiks atrisināts un atbildēs, lai tā saturu ievietotu rindā; katrs pavediens ir dēmons (neuzturēs procesu, ja galvenais pavediens beigsies - tas ir biežāk sastopams, nekā nav); galvenais pavediens iedarbina visus apakšvēstījumus, veic get
rindā, lai gaidītu, kamēr viens no tiem ir veicis put
, tad izdala rezultātus un izbeidz darbību (tādējādi izbeidz visus apakšvēstījumus, kas vēl varētu darboties, jo tie ir dēmonu pavedieni).
Pareiza pavedienu izmantošana Python ir vienmēr saistīta ar I/O operācijām (tā kā CPython tik un tā neizmanto vairākus kodolus, lai izpildītu uzdevumus, kas saistīti ar CPU, vienīgais pavedienu izmantošanas iemesls ir nebloķēt procesu, kamēr tiek gaidīts kāds I/O). Starp citu, rindas gandrīz vienmēr ir vislabākais veids, kā sadalīt darbu pavedieniem un/vai apkopot darba rezultātus, turklāt tās ir pēc būtības drošas, tāpēc jums nav jāuztraucas par atslēgām, nosacījumiem, notikumiem, semaforiem un citām pavedienu savstarpējās koordinācijas/saziņas koncepcijām.
PIEZĪME: Lai veiktu faktisko paralēlizāciju Python, jāizmanto multiprocessing modulis, lai izvērstu vairākus procesus, kas tiek izpildīti paralēli (globālā tulka bloķēšanas dēļ Python pavedieni nodrošina pārklāšanos, bet faktiski tiek izpildīti sērijveidā, nevis paralēli, un ir noderīgi tikai tad, ja tiek pārklātas I/O operācijas).
Tomēr, ja jūs tikai meklējat pārklāšanu (vai veicat I/O operācijas, kuras var veikt paralēli, neraugoties uz globālo tulka bloķēšanu), tad threading modulis ir vieta, kur sākt. Kā patiešām vienkāršu piemēru aplūkosim problēmu, kas saistīta ar liela diapazona summēšanu, paralēli saskaitot apakšrindas:
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
Ievērojiet, ka iepriekš minētais piemērs ir ļoti muļķīgs, jo tas neveic pilnīgi nekādu I/O un tiks izpildīts sērijveidā, lai gan ar pārklāšanos (ar papildu konteksta pārslēgšanas režiju) CPython globālā tulka bloķēšanas dēļ.
Kā jau citi minēja, CPython var izmantot pavedienus tikai I\O gaidīšanai GIL dēļ. Ja vēlaties izmantot vairāku kodolu priekšrocības uzdevumiem, kas saistīti ar CPU, izmantojiet multiprocessing:
from multiprocessing import Process
def f(name):
print 'hello', name
if __name__ == '__main__':
p = Process(target=f, args=('bob',))
p.start()
p.join()