Python'da iş parçacığı nasıl kullanılır?

Python'da iş parçacığını anlamaya çalışıyorum. Belgelere ve örneklere baktım, ama açıkçası, birçok örnek aşırı karmaşık ve onları anlamakta zorlanıyorum.

Çoklu iş parçacığı için görevlerin bölündüğünü nasıl açıkça gösterirsiniz?

İşte basit bir örnek: birkaç alternatif URL denemeniz ve yanıt veren ilk URL'nin içeriğini döndürmeniz gerekir.

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

Bu, iş parçacığının basit bir optimizasyon olarak kullanıldığı bir durumdur: her alt iş parçacığı, içeriğini kuyruğa koymak için bir URL'nin çözümlenmesini ve yanıt vermesini bekler; her iş parçacığı bir daemon'dur (ana iş parçacığı sona ererse süreci devam ettirmez - bu daha yaygındır); ana iş parçacığı tüm alt iş parçacıklarını başlatır, kuyrukta bir get' yapar ve bunlardan biriput' yapana kadar bekler, ardından sonuçları yayınlar ve sonlandırır (bu, daemon iş parçacıkları oldukları için hala çalışıyor olabilecek tüm alt iş parçacıklarını aşağı çeker).

Python'da iş parçacıklarının doğru kullanımı her zaman G/Ç işlemleriyle bağlantılıdır (CPython zaten CPU'ya bağlı görevleri çalıştırmak için birden fazla çekirdek kullanmadığından, iş parçacığının tek nedeni, bazı G/Ç için bekleme varken işlemi engellememektir). Bu arada, kuyruklar neredeyse her zaman işi iş parçacıklarına dağıtmanın ve/veya işin sonuçlarını toplamanın en iyi yoludur ve içsel olarak iş parçacığı güvenlidir, böylece sizi kilitler, koşullar, olaylar, semaforlar ve diğer iş parçacıkları arası koordinasyon / iletişim kavramları hakkında endişelenmekten kurtarırlar.

Yorumlar (14)

NOT: Python'da gerçek paralelleştirme için, paralel olarak çalışan birden fazla işlemi çatallamak için multiprocessing modülünü kullanmalısınız (global yorumlayıcı kilidi nedeniyle, Python iş parçacıkları serpiştirme sağlar, ancak aslında paralel olarak değil seri olarak yürütülür ve yalnızca G / Ç işlemlerini serpiştirirken kullanışlıdır).

Ancak, yalnızca serpiştirme arıyorsanız (veya global yorumlayıcı kilidine rağmen paralelleştirilebilen G/Ç işlemleri yapıyorsanız), o zaman threading modülü başlamanız gereken yerdir. Gerçekten basit bir örnek olarak, alt aralıkları paralel olarak toplayarak büyük bir aralığı toplama problemini ele alalım:

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

Yukarıdakinin çok aptalca bir örnek olduğunu unutmayın, çünkü kesinlikle hiçbir G/Ç yapmaz ve küresel yorumlayıcı kilidi nedeniyle CPython'da serpiştirilmiş olsa da (bağlam değiştirmenin ek yükü ile) seri olarak yürütülecektir.

Yorumlar (8)

Diğerlerinin de belirttiği gibi, CPython GIL nedeniyle iş parçacıklarını yalnızca I\O beklemeleri için kullanabilir. Eğer CPU'ya bağlı görevler için birden fazla çekirdekten faydalanmak istiyorsanız multiprocessing kullanın:

from multiprocessing import Process

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

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