پاسخ تمرین ۱
Pool یک گروه از پردازشهای کارگر میسازد تا توابع بهصورت موازی اجرا شوند. وقتی دادههای زیادی باید پردازش شوند مفید است، چون کار را بین چند پردازش پخش میکند و نتایج را جمعآوری میکند.
پاسخ تمرین ۲
pool.map تابع را روی هر عنصر لیست بهصورت موازی در پردازشهای موجود اجرا میکند و همهی نتایج را (به ترتیب ورودی) در یک لیست جمعآوری میکند.
پاسخ تمرین ۳
چون with پس از پایان کار، Pool را بهصورت خودکار میبندد و منابعش را آزاد میکند، بدون اینکه خودمان لازم باشد آن را ببندیم (جلوگیری از نشت منابع).
پاسخ تمرین ۴
مقداری که به پارامتر processes هنگام ساخت Pool داده میشود، تعداد پردازشهای همزمان را مشخص میکند. اگر آن را ندهیم، Pool بهصورت پیشفرض برابر تعداد هستههای پردازنده کارگر میسازد.
پاسخ تمرین ۵
from multiprocessing import Pool
def square(x):
return x * x
if __name__ == "__main__":
with Pool(processes=3) as pool:
results = pool.map(square, [1, 2, 3, 4, 5])
print(results) # [1, 4, 9, 16, 25]پاسخ تمرین ۶
from multiprocessing import Pool
def add(a, b):
return a + b
if __name__ == "__main__":
with Pool(processes=3) as pool:
results = pool.starmap(add, [(1, 2), (3, 4), (5, 6)])
print(results) # [3, 7, 11]map فقط یک آرگومان به تابع میدهد؛ چون add دو آرگومان میگیرد، به starmap نیاز داریم که هر عنصر را بهصورت tuple از آرگومانها باز میکند.
پاسخ تمرین ۷
import time
from multiprocessing import Pool
def heavy(n):
total = 0
for i in range(n):
total += i
return total
if __name__ == "__main__":
tasks = [10_000_000] * 4
start = time.time()
[heavy(t) for t in tasks]
print("sequential: %.2fs" % (time.time() - start))
start = time.time()
with Pool() as pool:
pool.map(heavy, tasks)
print("pool: %.2fs" % (time.time() - start)) # several times fasterپاسخ تمرین ۸
map مسدودکننده است و نتایج را یکجا و مرتب برمیگرداند. apply_async غیرمسدودکننده است و یک شیء نتیجه میدهد که با .get() خوانده میشود. imap نتایج را یکییکی و بهمحض آمادهشدن (بهصورت iterator) میدهد و برای ورودیهای خیلی بزرگ مناسب است. Pool برای کارهای CPU-bound مناسب است؛ برای I/O از threading یا asyncio استفاده کنید.