Python'da iş parçacığını anlamaya çalışıyorum. Belgelere ve örneklere baktım, ama açıkçası, birçok örnek aşırı derecede karmaşık ve onları anlamakta zorlanıyorum.
Çok iş parçacıklı işlem için bölünen görevleri nasıl açıkça gösterirsiniz?
Python'da iş parçacığını anlamaya çalışıyorum. Belgelere ve örneklere baktım, ama açıkçası, birçok örnek aşırı derecede karmaşık ve onları anlamakta zorlanıyorum.
Çok iş parçacıklı işlem için bölünen görevleri nasıl açıkça gösterirsiniz?
Yanıtlar:
Bu soru 2010 yılında sorulduğundan, harita ve havuzlu Python ile basit çoklu kullanımın nasıl yapılacağı konusunda gerçek bir basitlik olmuştur .
Aşağıdaki kod kesinlikle kontrol etmeniz gereken bir makale / blog gönderisinden gelir (bağlantı yok) - Paralellik tek bir satırda: Günlük Günlük İşler için Daha İyi Bir Model . Aşağıda özetleyeceğim - sadece birkaç satır kod olacak:
from multiprocessing.dummy import Pool as ThreadPool
pool = ThreadPool(4)
results = pool.map(my_function, my_array)
Hangi çok iş parçacıklı sürümü:
results = []
for item in my_array:
results.append(my_function(item))
Açıklama
Harita harika bir küçük işlevdir ve Python kodunuza kolayca paralellik enjekte etmenin anahtarıdır. Bilmeyenler için harita Lisp gibi işlevsel dillerden kaldırılmış bir şeydir. Bir sekans üzerinde başka bir fonksiyonu eşleyen bir fonksiyondur.
Harita, yinelemeyi bizim için dizi üzerinde işler, işlevi uygular ve tüm sonuçları sonunda kullanışlı bir listede saklar.
uygulama
Harita işlevinin paralel sürümleri iki kütüphane tarafından sağlanır: çoklu işlem ve aynı zamanda az bilinen, ancak aynı derecede fantastik adım çocuğu: multiprocessing.dummy.
multiprocessing.dummy
çok işlemcili modülle tamamen aynıdır, ancak bunun yerine iş parçacıklarını kullanır ( önemli bir ayrım - CPU yoğun görevler için birden çok işlem kullanın; G / Ç için iş parçacıkları (ve sırasında ):
multiprocessing.dummy, çoklu işlem API'sini çoğaltır, ancak iş parçacığı modülü etrafındaki bir paketleyiciden başka bir şey değildir.
import urllib2
from multiprocessing.dummy import Pool as ThreadPool
urls = [
'http://www.python.org',
'http://www.python.org/about/',
'http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html',
'http://www.python.org/doc/',
'http://www.python.org/download/',
'http://www.python.org/getit/',
'http://www.python.org/community/',
'https://wiki.python.org/moin/',
]
# Make the Pool of workers
pool = ThreadPool(4)
# Open the URLs in their own threads
# and return the results
results = pool.map(urllib2.urlopen, urls)
# Close the pool and wait for the work to finish
pool.close()
pool.join()
Ve zamanlama sonuçları:
Single thread: 14.4 seconds
4 Pool: 3.1 seconds
8 Pool: 1.4 seconds
13 Pool: 1.3 seconds
Birden çok argüman iletme ( yalnızca Python 3.3 ve sonraki sürümlerde böyle çalışır ):
Birden çok diziyi geçmek için:
results = pool.starmap(function, zip(list_a, list_b))
Veya bir sabit ve bir dizi iletmek için:
results = pool.starmap(function, zip(itertools.repeat(constant), list_a))
Python'un önceki bir sürümünü kullanıyorsanız, bu geçici çözüm üzerinden birden çok bağımsız değişken iletebilirsiniz ).
( Yararlı yorum için user136036'ya teşekkürler .)
with Pool(8) as p: p.map( *whatever* )
da muhasebe defterleri yazabilir ve kurtulabilirsiniz.
İşte basit bir örnek: Birkaç alternatif URL denemeniz ve yanıt vermek için ilk URL'nin içeriğini döndürmeniz gerekiyor.
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 iplik, içeriğini sıraya koymak için bir URL'nin çözümlenmesini ve yanıt vermesini bekler; her bir iş parçacığı bir daemon'dur (ana iş parçacığı biterse süreci devam ettirmez - bu daha yaygın değildir); ana iş parçacığı tüm alt konuları başlatır get
, bunlardan biri yapılana kadar beklemek için kuyrukta bir a yapar put
, sonra sonuçları yayar ve sonlandırır (bu daemon iş parçacıkları oldukları için hala çalışmakta olan alt başlıkları alır).
Python'da iş parçacıklarının doğru kullanımı her zaman G / Ç işlemlerine bağlıdır (CPython zaten CPU'ya bağlı görevleri çalıştırmak için birden fazla çekirdek kullanmadığından, diş açmanın tek nedeni bazı G / Ç'leri beklerken işlemi engellememektir. ). Kuyruklar neredeyse her zaman işi iş parçacıklarına ayırmanın ve / veya işin sonuçlarını toplamanın en iyi yoludur ve kendiliğinden iş parçacığı açısından güvenlidirler, böylece sizi kilitler, koşullar, olaylar, semaforlar ve diğer iç ortamlar hakkında endişelenmekten kurtarırlar. iplik koordinasyonu / iletişim kavramları.
join()
yöntemi kullanmak olacağını düşünüyorum , çünkü bu işlem ana işlemciyi sürekli olarak işlemci tüketmeden bitene kadar bekletecektir. değeri kontrol. @Alex: teşekkürler, iş parçacıklarını nasıl kullanacağımı anlamak için tam da ihtiyacım olan şey bu.
Queue
modül adını değiştirin queue
. Yöntem adı aynı.
s = q.get()
print s
@ krs013 join
Çünkü Queue.get () engelleniyor.
NOT : Python'da gerçek paralelleştirme için, paralel olarak çalışan birden çok işlemi çatallamak için çok işlem modülünü kullanmalısınız (global yorumlayıcı kilidi nedeniyle, Python iş parçacıkları araya ekleme sağlar, ancak aslında paralel olarak değil seri olarak yürütülür ve yalnızca G / Ç işlemlerini harmanlarken 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), iş parçacığı modülü başlangıç noktasıdır. Gerçekten basit bir örnek olarak, alt aralıkları paralel olarak toplayarak geniş bir aralığı toplama sorununu 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ıdakilerin kesinlikle hiçbir I / O yapmadığı ve global yorumlayıcı kilidi nedeniyle CPython'da serpiştirilmiş olsa da (bağlam anahtarlama ek yükü ile) seri olarak yürütüleceğinden çok aptal bir örnek olduğuna dikkat edin.
thread1
ana iş parçacığı bloke edilirken tamamlanana kadar çalışır, sonra aynı şey olur thread2
, daha sonra ana iş parçacığı devam eder ve biriktirdikleri değerleri yazdırır.
super(SummingThread, self).__init__()
? Stackoverflow.com/a/2197625/806988'de
Belirtilen diğerleri gibi, CPython yalnızca GIL nedeniyle G / Ç beklemeleri için iş parçacığı kullanabilir .
CPU'ya bağlı görevler için birden çok çekirdekten yararlanmak istiyorsanız, çoklu işleme 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()
f
fonksiyon. Buna paralel olarak, ana program şimdi sürecin kendisinden çıkmasını beklemektedir join
. Ana parça yeni çıkmışsa, alt işlem tamamlanabilir veya tamamlanmayabilir, bu nedenle a'nın join
yapılması her zaman önerilir.
map
İşlevi içeren genişletilmiş bir yanıt burada: stackoverflow.com/a/28463266/2327328
Sadece bir not: İplik geçirme için kuyruk gerekmez.
Bu, eşzamanlı olarak çalışan 10 işlemi gösterdiğini hayal edebileceğim en basit örnektir.
import threading
from random import randint
from time import sleep
def print_number(number):
# Sleeps a random 1 to 10 seconds
rand_int_var = randint(1, 10)
sleep(rand_int_var)
print "Thread " + str(number) + " slept for " + str(rand_int_var) + " seconds"
thread_list = []
for i in range(1, 10):
# Instantiates the thread
# (i) does not make a sequence, so (i,)
t = threading.Thread(target=print_number, args=(i,))
# Sticks the thread in a list so that it remains accessible
thread_list.append(t)
# Starts threads
for thread in thread_list:
thread.start()
# This blocks the calling thread until the thread whose join() method is called is terminated.
# From http://docs.python.org/2/library/threading.html#thread-objects
for thread in thread_list:
thread.join()
# Demonstrates that the main process waited for threads to complete
print "Done"
for
loop'a gerek yok thread.start()
, ilk loop'u arayabilirsiniz .
Alex Martelli'nin cevabı bana yardımcı oldu. Ancak, burada daha yararlı olduğunu düşündüğüm değiştirilmiş bir versiyon (en azından benim için).
Güncellendi: Python 2 ve Python 3'te çalışır
try:
# For Python 3
import queue
from urllib.request import urlopen
except:
# For Python 2
import Queue as queue
from urllib2 import urlopen
import threading
worker_data = ['http://google.com', 'http://yahoo.com', 'http://bing.com']
# Load up a queue with your data. This will handle locking
q = queue.Queue()
for url in worker_data:
q.put(url)
# Define a worker function
def worker(url_queue):
queue_full = True
while queue_full:
try:
# Get your data off the queue, and do some work
url = url_queue.get(False)
data = urlopen(url).read()
print(len(data))
except queue.Empty:
queue_full = False
# Create as many threads as you want
thread_count = 5
for i in range(thread_count):
t = threading.Thread(target=worker, args = (q,))
t.start()
import Queue ModuleNotFoundError: No module named 'Queue'
Bazı mesajlar python 3.6.5 çalıştırıyorum python 3.6.5 olduğunu söyledi queue
ama ben değiştirdikten sonra bile, hala çalışmıyor
Bir işlev verildiğinde şu şekilde işleyin f
:
import threading
threading.Thread(target=f).start()
Argümanlarını f
threading.Thread(target=f, args=(a,b,c)).start()
is_alive
Yöntemi gördüm , ancak iş parçacığına nasıl uygulanacağını anlayamadım. Atamayı thread1=threading.Thread(target=f).start()
ve sonra kontrol etmeyi denedim thread1.is_alive()
, ancak thread1
nüfuslu None
, bu yüzden orada şans yok. Konuya erişmenin başka bir yolu olup olmadığını biliyor musunuz?
thread1=threading.Thread(target=f)
ardından onu kullanarak başlatmanız gerekir thread1.start()
. Sonra yapabilirsin thread1.is_alive()
.
thread1.is_alive()
geri dönüş False
yapar.
Bu çok yararlı buldum: çekirdekler gibi çok sayıda iş parçacığı oluşturmak ve (bu durumda, bir kabuk programı çağırarak) görevleri (çok sayıda) yürütmek izin:
import Queue
import threading
import multiprocessing
import subprocess
q = Queue.Queue()
for i in range(30): # Put 30 tasks in the queue
q.put(i)
def worker():
while True:
item = q.get()
# Execute a task: call a shell program and wait until it completes
subprocess.call("echo " + str(item), shell=True)
q.task_done()
cpus = multiprocessing.cpu_count() # Detect number of cores
print("Creating %d threads" % cpus)
for i in range(cpus):
t = threading.Thread(target=worker)
t.daemon = True
t.start()
q.join() # Block until all tasks are done
Python 3, paralel görevler başlatma imkanına sahiptir . Bu işimizi kolaylaştırır.
İş parçacığı havuzu ve işlem havuzu vardır .
Aşağıdakiler bir fikir verir:
ThreadPoolExecutor Örneği ( kaynak )
import concurrent.futures
import urllib.request
URLS = ['http://www.foxnews.com/',
'http://www.cnn.com/',
'http://europe.wsj.com/',
'http://www.bbc.co.uk/',
'http://some-made-up-domain.com/']
# Retrieve a single page and report the URL and contents
def load_url(url, timeout):
with urllib.request.urlopen(url, timeout=timeout) as conn:
return conn.read()
# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
# Start the load operations and mark each future with its URL
future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
for future in concurrent.futures.as_completed(future_to_url):
url = future_to_url[future]
try:
data = future.result()
except Exception as exc:
print('%r generated an exception: %s' % (url, exc))
else:
print('%r page is %d bytes' % (url, len(data)))
ProcessPoolExecutor ( kaynak )
import concurrent.futures
import math
PRIMES = [
112272535095293,
112582705942171,
112272535095293,
115280095190773,
115797848077099,
1099726899285419]
def is_prime(n):
if n % 2 == 0:
return False
sqrt_n = int(math.floor(math.sqrt(n)))
for i in range(3, sqrt_n + 1, 2):
if n % i == 0:
return False
return True
def main():
with concurrent.futures.ProcessPoolExecutor() as executor:
for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
print('%d is prime: %s' % (number, prime))
if __name__ == '__main__':
main()
Çarpıcı yeni eşzamanlı.futures modülünü kullanma
def sqr(val):
import time
time.sleep(0.1)
return val * val
def process_result(result):
print(result)
def process_these_asap(tasks):
import concurrent.futures
with concurrent.futures.ProcessPoolExecutor() as executor:
futures = []
for task in tasks:
futures.append(executor.submit(sqr, task))
for future in concurrent.futures.as_completed(futures):
process_result(future.result())
# Or instead of all this just do:
# results = executor.map(sqr, tasks)
# list(map(process_result, results))
def main():
tasks = list(range(10))
print('Processing {} tasks'.format(len(tasks)))
process_these_asap(tasks)
print('Done')
return 0
if __name__ == '__main__':
import sys
sys.exit(main())
Yürütücü yaklaşımı, daha önce Java ile ellerini kirleten herkese tanıdık gelebilir.
Ayrıca bir yan notta: Evren aklı başında tutmak için, with
bağlam kullanmıyorsanız havuzlarınızı / uygulayıcılarınızı kapatmayı unutmayın (bu sizin için çok harika bir şeydir)
Benim için, diş çekme için mükemmel örnek asenkron olayları izlemek. Bu koda bak.
# thread_test.py
import threading
import time
class Monitor(threading.Thread):
def __init__(self, mon):
threading.Thread.__init__(self)
self.mon = mon
def run(self):
while True:
if self.mon[0] == 2:
print "Mon = 2"
self.mon[0] = 3;
Bir IPython oturumu açıp aşağıdaki gibi bir şey yaparak bu kodla oynayabilirsiniz :
>>> from thread_test import Monitor
>>> a = [0]
>>> mon = Monitor(a)
>>> mon.start()
>>> a[0] = 2
Mon = 2
>>>a[0] = 2
Mon = 2
Bir kaç dakika bekle
>>> a[0] = 2
Mon = 2
Çoğu belge ve öğretici Python Threading
veQueue
modülünü yeni başlayanlar için bunaltıcı görünebilir.
Belki de concurrent.futures.ThreadPoolExecutor
Python 3 modülünü .
İle birlikte with
fıkra ve liste anlama gerçek bir çekicilik olabilir.
from concurrent.futures import ThreadPoolExecutor, as_completed
def get_url(url):
# Your actual program here. Using threading.Lock() if necessary
return ""
# List of URLs to fetch
urls = ["url1", "url2"]
with ThreadPoolExecutor(max_workers = 5) as executor:
# Create threads
futures = {executor.submit(get_url, url) for url in urls}
# as_completed() gives you the threads once finished
for f in as_completed(futures):
# Get the results
rs = f.result()
Burada gerçek bir işin yapılmadığı birçok örnek gördüm ve çoğunlukla CPU'ya bağlıydı. İşte 10 milyon ila 10,05 milyon arasındaki tüm asal sayıları hesaplayan CPU'ya bağlı bir görev örneği. Burada dört yöntemi de kullandım:
import math
import timeit
import threading
import multiprocessing
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
def time_stuff(fn):
"""
Measure time of execution of a function
"""
def wrapper(*args, **kwargs):
t0 = timeit.default_timer()
fn(*args, **kwargs)
t1 = timeit.default_timer()
print("{} seconds".format(t1 - t0))
return wrapper
def find_primes_in(nmin, nmax):
"""
Compute a list of prime numbers between the given minimum and maximum arguments
"""
primes = []
# Loop from minimum to maximum
for current in range(nmin, nmax + 1):
# Take the square root of the current number
sqrt_n = int(math.sqrt(current))
found = False
# Check if the any number from 2 to the square root + 1 divides the current numnber under consideration
for number in range(2, sqrt_n + 1):
# If divisible we have found a factor, hence this is not a prime number, lets move to the next one
if current % number == 0:
found = True
break
# If not divisible, add this number to the list of primes that we have found so far
if not found:
primes.append(current)
# I am merely printing the length of the array containing all the primes, but feel free to do what you want
print(len(primes))
@time_stuff
def sequential_prime_finder(nmin, nmax):
"""
Use the main process and main thread to compute everything in this case
"""
find_primes_in(nmin, nmax)
@time_stuff
def threading_prime_finder(nmin, nmax):
"""
If the minimum is 1000 and the maximum is 2000 and we have four workers,
1000 - 1250 to worker 1
1250 - 1500 to worker 2
1500 - 1750 to worker 3
1750 - 2000 to worker 4
so let’s split the minimum and maximum values according to the number of workers
"""
nrange = nmax - nmin
threads = []
for i in range(8):
start = int(nmin + i * nrange/8)
end = int(nmin + (i + 1) * nrange/8)
# Start the thread with the minimum and maximum split up to compute
# Parallel computation will not work here due to the GIL since this is a CPU-bound task
t = threading.Thread(target = find_primes_in, args = (start, end))
threads.append(t)
t.start()
# Don’t forget to wait for the threads to finish
for t in threads:
t.join()
@time_stuff
def processing_prime_finder(nmin, nmax):
"""
Split the minimum, maximum interval similar to the threading method above, but use processes this time
"""
nrange = nmax - nmin
processes = []
for i in range(8):
start = int(nmin + i * nrange/8)
end = int(nmin + (i + 1) * nrange/8)
p = multiprocessing.Process(target = find_primes_in, args = (start, end))
processes.append(p)
p.start()
for p in processes:
p.join()
@time_stuff
def thread_executor_prime_finder(nmin, nmax):
"""
Split the min max interval similar to the threading method, but use a thread pool executor this time.
This method is slightly faster than using pure threading as the pools manage threads more efficiently.
This method is still slow due to the GIL limitations since we are doing a CPU-bound task.
"""
nrange = nmax - nmin
with ThreadPoolExecutor(max_workers = 8) as e:
for i in range(8):
start = int(nmin + i * nrange/8)
end = int(nmin + (i + 1) * nrange/8)
e.submit(find_primes_in, start, end)
@time_stuff
def process_executor_prime_finder(nmin, nmax):
"""
Split the min max interval similar to the threading method, but use the process pool executor.
This is the fastest method recorded so far as it manages process efficiently + overcomes GIL limitations.
RECOMMENDED METHOD FOR CPU-BOUND TASKS
"""
nrange = nmax - nmin
with ProcessPoolExecutor(max_workers = 8) as e:
for i in range(8):
start = int(nmin + i * nrange/8)
end = int(nmin + (i + 1) * nrange/8)
e.submit(find_primes_in, start, end)
def main():
nmin = int(1e7)
nmax = int(1.05e7)
print("Sequential Prime Finder Starting")
sequential_prime_finder(nmin, nmax)
print("Threading Prime Finder Starting")
threading_prime_finder(nmin, nmax)
print("Processing Prime Finder Starting")
processing_prime_finder(nmin, nmax)
print("Thread Executor Prime Finder Starting")
thread_executor_prime_finder(nmin, nmax)
print("Process Executor Finder Starting")
process_executor_prime_finder(nmin, nmax)
main()
İşte Mac OS X dört çekirdekli makinemdeki sonuçlar
Sequential Prime Finder Starting
9.708213827005238 seconds
Threading Prime Finder Starting
9.81836523200036 seconds
Processing Prime Finder Starting
3.2467174359990167 seconds
Thread Executor Prime Finder Starting
10.228896902000997 seconds
Process Executor Finder Starting
2.656402041000547 seconds
if __name__ == '__main__':
aksi ölçüm yumurtlar kendisi ve baskılar, ana aramadan önce denemesi yapılmıştır önce yeni bir süreç başlatmak için ... .
İş parçacığı kullanarak CSV içe aktarmanın çok basit bir örneği . (Kütüphaneye katılım farklı amaçlar için farklılık gösterebilir.)
Yardımcı İşlevleri:
from threading import Thread
from project import app
import csv
def import_handler(csv_file_name):
thr = Thread(target=dump_async_csv_data, args=[csv_file_name])
thr.start()
def dump_async_csv_data(csv_file_name):
with app.app_context():
with open(csv_file_name) as File:
reader = csv.DictReader(File)
for row in reader:
# DB operation/query
Sürücü Fonksiyonu:
import_handler(csv_file_name)
Basit bir örnek ve bu sorunu kendim halletmem gerektiğinde yararlı bulduğum açıklamalar ile katkıda bulunmak istiyorum.
Bu cevapta Python'un GIL (global tercüman kilidi) hakkında bazı bilgiler ve multiprocessing.dummy ve bazı basit karşılaştırmalar kullanılarak yazılmış basit bir günlük örnek bulacaksınız .
Global Tercüman Kilidi (GIL)
Python, kelimenin tam anlamıyla çoklu iş parçacığına izin vermez. Çok iş parçacıklı bir pakete sahiptir, ancak kodunuzu hızlandırmak için çok iş parçacıklı bir paket istiyorsanız, bunu kullanmak genellikle iyi bir fikir değildir.
Python'un global yorumlayıcı kilidi (GIL) adı verilen bir yapısı vardır. GIL herhangi bir anda 'iş parçacıklarınızdan' yalnızca birinin çalışabilmesini sağlar. Bir iş parçacığı GIL'i alır, küçük bir iş yapar, sonra GIL'i bir sonraki iş parçacığına geçirir.
Bu çok hızlı bir şekilde gerçekleşir, böylece insan gözü için iplikleriniz paralel olarak çalışıyor gibi görünebilir, ancak aynı CPU çekirdeğini kullanarak sırayla.
Tüm bu GIL geçişi yürütmeye ek yük getirir. Bu, kodunuzun daha hızlı çalışmasını istiyorsanız, iş parçacığı paketini kullanmanın genellikle iyi bir fikir olmadığı anlamına gelir.
Python'un iş parçacığı paketini kullanmanın nedenleri var. Bazı şeyleri aynı anda çalıştırmak istiyorsanız ve verimlilik bir endişe kaynağı değilse, tamamen iyi ve kullanışlıdır. Veya bir şey (bazı G / Ç gibi) beklemesi gereken bir kod çalıştırıyorsanız, bu çok mantıklı olabilir. Ancak iş parçacığı kütüphanesi ekstra CPU çekirdeği kullanmanıza izin vermez.
Çoklu iş parçacığı, işletim sistemine (çoklu işlem yaparak) ve Python kodunuzu (örneğin, Spark veya Hadoop ) çağıran bazı harici uygulamalara veya Python kodunuzun çağırdığı bazı kodlara (örneğin: Python kodunuzun pahalı çok iş parçacıklı şeyleri yapan bir C işlevini çağırmasını sağlayın).
Bu Neden Önemli?
Çünkü birçok insan, GIL'in ne olduğunu öğrenmeden önce süslü Python çok iş parçacıklı kodlarında darboğazlar bulmaya çalışırken çok zaman harcıyor.
Bu bilgiler netleştikten sonra işte kodum:
#!/bin/python
from multiprocessing.dummy import Pool
from subprocess import PIPE,Popen
import time
import os
# In the variable pool_size we define the "parallelness".
# For CPU-bound tasks, it doesn't make sense to create more Pool processes
# than you have cores to run them on.
#
# On the other hand, if you are using I/O-bound tasks, it may make sense
# to create a quite a few more Pool processes than cores, since the processes
# will probably spend most their time blocked (waiting for I/O to complete).
pool_size = 8
def do_ping(ip):
if os.name == 'nt':
print ("Using Windows Ping to " + ip)
proc = Popen(['ping', ip], stdout=PIPE)
return proc.communicate()[0]
else:
print ("Using Linux / Unix Ping to " + ip)
proc = Popen(['ping', ip, '-c', '4'], stdout=PIPE)
return proc.communicate()[0]
os.system('cls' if os.name=='nt' else 'clear')
print ("Running using threads\n")
start_time = time.time()
pool = Pool(pool_size)
website_names = ["www.google.com","www.facebook.com","www.pinterest.com","www.microsoft.com"]
result = {}
for website_name in website_names:
result[website_name] = pool.apply_async(do_ping, args=(website_name,))
pool.close()
pool.join()
print ("\n--- Execution took {} seconds ---".format((time.time() - start_time)))
# Now we do the same without threading, just to compare time
print ("\nRunning NOT using threads\n")
start_time = time.time()
for website_name in website_names:
do_ping(website_name)
print ("\n--- Execution took {} seconds ---".format((time.time() - start_time)))
# Here's one way to print the final output from the threads
output = {}
for key, value in result.items():
output[key] = value.get()
print ("\nOutput aggregated in a Dictionary:")
print (output)
print ("\n")
print ("\nPretty printed output: ")
for key, value in output.items():
print (key + "\n")
print (value)
İşte size yardımcı olacak basit bir örnek ile çoklu iş parçacığı. Bunu çalıştırabilir ve Python'da çoklu iş parçacığının nasıl çalıştığını kolayca anlayabilirsiniz. Önceki iş parçacığı çalışmalarını bitirinceye kadar diğer iş parçacıklarına erişimi engellemek için bir kilit kullandım. Bu kod satırını kullanarak,
tLock = threading.BoundedSemaphore (değer = 4)
bir seferde birkaç işleme izin verebilir ve daha sonra veya önceki işlemlerin bitiminden sonra çalışacak olan diğer iş parçacıklarını tutabilirsiniz.
import threading
import time
#tLock = threading.Lock()
tLock = threading.BoundedSemaphore(value=4)
def timer(name, delay, repeat):
print "\r\nTimer: ", name, " Started"
tLock.acquire()
print "\r\n", name, " has the acquired the lock"
while repeat > 0:
time.sleep(delay)
print "\r\n", name, ": ", str(time.ctime(time.time()))
repeat -= 1
print "\r\n", name, " is releaseing the lock"
tLock.release()
print "\r\nTimer: ", name, " Completed"
def Main():
t1 = threading.Thread(target=timer, args=("Timer1", 2, 5))
t2 = threading.Thread(target=timer, args=("Timer2", 3, 5))
t3 = threading.Thread(target=timer, args=("Timer3", 4, 5))
t4 = threading.Thread(target=timer, args=("Timer4", 5, 5))
t5 = threading.Thread(target=timer, args=("Timer5", 0.1, 5))
t1.start()
t2.start()
t3.start()
t4.start()
t5.start()
print "\r\nMain Complete"
if __name__ == "__main__":
Main()
Borç ile bu yazı biz çoklu işlem, çoklu arasında seçim biliyorum ve zaman uyumsuz / asyncio
ve bunların kullanımı.
Python 3 , eşzamanlılık ve paralellik için yeni bir yerleşik kütüphaneye sahiptir: concurrent.futures
Bu yüzden dört görevi (yani .sleep()
yöntemi) çalıştırmak için bir deney yoluyla Threading-Pool
göstereceğim:
from concurrent.futures import ThreadPoolExecutor, as_completed
from time import sleep, time
def concurrent(max_worker=1):
futures = []
tick = time()
with ThreadPoolExecutor(max_workers=max_worker) as executor:
futures.append(executor.submit(sleep, 2)) # Two seconds sleep
futures.append(executor.submit(sleep, 1))
futures.append(executor.submit(sleep, 7))
futures.append(executor.submit(sleep, 3))
for future in as_completed(futures):
if future.result() is not None:
print(future.result())
print('Total elapsed time by {} workers:'.format(max_worker), time()-tick)
concurrent(5)
concurrent(4)
concurrent(3)
concurrent(2)
concurrent(1)
Çıktı:
Total elapsed time by 5 workers: 7.007831811904907
Total elapsed time by 4 workers: 7.007944107055664
Total elapsed time by 3 workers: 7.003149509429932
Total elapsed time by 2 workers: 8.004627466201782
Total elapsed time by 1 workers: 13.013478994369507
[ NOT ]:
multiprocessing
vs threading
değiştirmek olabilir) ThreadPoolExecutor
için ProcessPoolExecutor
.Önceki çözümlerin hiçbiri GNU / Linux sunucumda birden fazla çekirdek kullanmadı (burada yönetici haklarına sahip değilim). Sadece tek bir çekirdek üzerinde koştular.
os.fork
Birden çok işlemi ortaya çıkarmak için alt düzey arayüzü kullandım. Bu benim için çalışan kod:
from os import fork
values = ['different', 'values', 'for', 'threads']
for i in range(len(values)):
p = fork()
if p == 0:
my_function(values[i])
break
import threading
import requests
def send():
r = requests.get('https://www.stackoverlow.com')
thread = []
t = threading.Thread(target=send())
thread.append(t)
t.start()