Muhtemelen paralel işlemeye ihtiyaç duyduğunuzda çoğu zaman, ya modüldeki ProcessPoolExecutor
sınıfın ya da concurrent.futures
modüldeki Pool
sınıfın multiprocessing
eşdeğer kolaylıklar sağlayacağını ve kişisel tercih meselesine dönüştüğünü göreceksiniz . Ancak her biri, belirli işlemleri daha kolay hale getiren bazı olanaklar sunar. Sadece bir çift göstereceğimi düşündüm:
Bir grup görevi gönderirken, bazen görev sonuçlarını (yani değerleri döndürür) kullanılabilir olur olmaz almak istersiniz. Her iki tesis de, gönderilen bir görevin sonucunun geri arama mekanizmaları aracılığıyla kullanılabilir olduğuna dair bildirim sağlar:
Çoklu işlemeyi kullanma.Havuz:
import multiprocessing as mp
def worker_process(i):
return i * i
def process_result(return_value):
print(return_value)
def main():
pool = mp.Pool()
for i in range(10):
pool.apply_async(worker_process, args=(i,), callback=process_result)
pool.close()
pool.join()
if __name__ == '__main__':
main()
Aynısı, garip bir şekilde de olsa, aşağıdakilerle bir geri arama kullanılarak yapılabilir concurrent.futures
:
import concurrent.futures
def worker_process(i):
return i * i
def process_result(future):
print(future.result())
def main():
executor = concurrent.futures.ProcessPoolExecutor()
futures = [executor.submit(worker_process, i) for i in range(10)]
for future in futures:
future.add_done_callback(process_result)
executor.shutdown()
if __name__ == '__main__':
main()
Burada her görev ayrı ayrı gönderilir ve bunun için bir Future
örnek döndürülür. Daha sonra geri arama Future
,. Son olarak, geri çağırma çağrıldığında, geçirilen bağımsız değişken, Future
tamamlanan görevin örneğidir result
ve gerçek dönüş değerini almak için yöntemin çağrılması gerekir. Ancak concurrent.futures
modül ile, aslında bir geri aramayı kullanmaya gerek yoktur. as_completed
Yöntemi kullanabilirsiniz :
import concurrent.futures
def worker_process(i):
return i * i
def main():
with concurrent.futures.ProcessPoolExecutor() as executor:
futures = [executor.submit(worker_process, i) for i in range(10)]
for future in concurrent.futures.as_completed(futures):
print(future.result())
if __name__ == '__main__':
main()
Ve örnekleri worker_process
tutmak için bir sözlük kullanarak , dönüş değerini orijinal geçirilen bağımsız değişkene geri bağlamak kolaydır Future
:
import concurrent.futures
def worker_process(i):
return i * i
def main():
with concurrent.futures.ProcessPoolExecutor() as executor:
futures = {executor.submit(worker_process, i): i for i in range(10)}
for future in concurrent.futures.as_completed(futures):
i = futures[future]
print(i, future.result())
if __name__ == '__main__':
main()
Ancak aşılmaması için, multiprocessing.Pool
görev sonuçlarının tamamlanırken işlenmesine izin veren bir yöntemi vardır:
import multiprocessing as mp
def worker_process(i):
return i * i
def main():
cpu_count = mp.cpu_count()
N = 100
chunk_size = N // cpu_count
with mp.Pool() as pool:
for result in pool.imap_unordered(worker_process, range(N), chunksize=chunk_size):
print(result)
if __name__ == '__main__':
main()
Ancak imap_unordered
, çalışan işlem dönüş değeriyle birlikte orijinal çağrı bağımsız değişkenlerini döndürmedikçe, bir sonucu gönderilen bir işle kolayca bağlamanın bir yolu yoktur. Öte yandan, sonuçların öngörülebilir bir sırada olacağı bir chunksize
ile imap_unorderd
ve belirtme yeteneği , bu yöntemleri , esasen 1'lik bir yığın boyutu kullanan sınıfın yönteminden daha verimli hale getirebilir .imap
submit
concurrent.futures.ProcessPoolExector
multiprocessing.Pool
Sınıf bir yöntemi vardır apply
sonuç hazır olana kadar havuz ve bloklar bir görev gönderir. Dönüş değeri, işleve aktarılan işçi işlevinin yalnızca dönüş değeridir apply
. Örneğin:
import multiprocessing as mp
def worker_process(i):
return i * i
def main():
with mp.Pool() as pool:
print(pool.apply(worker_process, args=(6,)))
print(pool.apply(worker_process, args=(4,)))
if __name__ == '__main__':
main()
concurrent.futures.ThreadPoolExecutor
Sınıf böyle bir eşdeğeri yer alır. Döndürülen örneğe bir submit
ve ardından bir çağrı yapmalısınız . Bunu yapmak zor değil, ancak yöntem, engelleyici bir görev gönderiminin uygun olduğu kullanım durumu için daha uygundur. Böyle bir durum, iş parçacığı için çağrıları işlediğiniz zamandır çünkü iş parçacıklarında yapılan işlerin çoğu, CPU'ya çok bağlı olan bir işlev haricinde yoğun bir şekilde I / O'dur. Evreleri oluşturan ana program önce bir örnek yaratır ve bunu tüm evrelere argüman olarak iletir. İş parçacığının yoğun şekilde CPU'ya bağlı işlevi çağırması gerektiğinde, şimdi işlevi kullanarakresult
Future
pool.apply
multiprocessing.Pool
pool.apply
yöntem böylece kodu başka bir işlemde çalıştırır ve diğer iş parçacıklarının çalışmasına izin vermek için mevcut işlemi serbest bırakır.
concurrent.futures
Modülün iki sınıfa ProcessPoolExecutor
ve ThreadPoolExecutor
aynı arayüzlere sahip olması büyük bir anlaşma yapıldı . Bu güzel bir özellik. Ancak multiprocessing
modül, aşağıdakilerle ThreadPool
aynı arayüze sahip belgelenmemiş bir sınıfa sahiptir Pool
:
>>> from multiprocessing.pool import Pool
>>> from multiprocessing.pool import ThreadPool
>>> dir(Pool)
['Process', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_check_running', '_get_sentinels', '_get_tasks', '_get_worker_sentinels', '_guarded_task_generation', '_handle_results', '_handle_tasks', '_handle_workers', '_help_stuff_finish', '_join_exited_workers', '_maintain_pool', '_map_async', '_repopulate_pool', '_repopulate_pool_static', '_setup_queues', '_terminate_pool', '_wait_for_updates', '_wrap_exception', 'apply', 'apply_async', 'close', 'imap', 'imap_unordered', 'join', 'map', 'map_async', 'starmap', 'starmap_async', 'terminate']
>>> dir(ThreadPool)
['Process', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_check_running', '_get_sentinels', '_get_tasks', '_get_worker_sentinels', '_guarded_task_generation', '_handle_results', '_handle_tasks', '_handle_workers', '_help_stuff_finish', '_join_exited_workers', '_maintain_pool', '_map_async', '_repopulate_pool', '_repopulate_pool_static', '_setup_queues', '_terminate_pool', '_wait_for_updates', '_wrap_exception', 'apply', 'apply_async', 'close', 'imap', 'imap_unordered', 'join', 'map', 'map_async', 'starmap', 'starmap_async', 'terminate']
>>>
ProcessPoolExecutor.submit
Bir Future
örnek döndüren veya Pool.apply_async
bir AsyncResult
örnek döndüren ve sonucu almak için bir zaman aşımı değeri belirleyen görevler ile görev gönderebilirsiniz :
from concurrent.futures import ProcessPoolExecutor, TimeoutError
from time import sleep
def worker_1():
while True:
print('hanging')
sleep(1)
def main():
with ProcessPoolExecutor(1) as pool:
future = pool.submit(worker_1)
try:
future.result(3)
except TimeoutError:
print('timeout')
if __name__ == '__main__':
main()
print("return from main()")
Baskılar:
hanging
hanging
hanging
timeout
hanging
hanging
hanging
hanging
hanging
hanging
hanging
etc.
Gönderilen görev bu süre içinde tamamlanmadığından , arama sırasındaki ana işlem 3 saniye sonra future.result(3)
bir TimeoutError
istisna alır. Ancak görev çalışmaya devam ediyor, süreci bağlar ve with ProcessPoolExecutor(1) as pool:
blok asla çıkmaz ve bu nedenle program sona ermez.
from multiprocessing import Pool, TimeoutError
from time import sleep
def worker_1():
while True:
print('hanging')
sleep(1)
def main():
with Pool(1) as pool:
result = pool.apply_async(worker_1, args=())
try:
result.get(3)
except TimeoutError:
print('timeout')
if __name__ == '__main__':
main()
print("return from main()")
Baskılar:
hanging
hanging
hanging
timeout
return from main()
Ancak bu sefer, zaman aşımına uğrayan görev hala çalışmaya devam ediyor ve işlemi yazıyor olsa bile, with
bloğun çıkması engellenmez ve bu nedenle program normal şekilde sona erer. Avantaj gidecek gibi görünüyor multiprocessing.Pool
.