multiprocessing.Pool: Kapan harus menggunakan apply, apply_async atau map?

Jawaban:

424

Kembali ke masa lalu Python, untuk memanggil fungsi dengan argumen arbitrer, Anda akan menggunakan apply:

apply(f,args,kwargs)

applymasih ada di Python2.7 meskipun tidak di Python3, dan umumnya tidak digunakan lagi. Sekarang,

f(*args,**kwargs)

lebih disukai. The multiprocessing.Poolmodul mencoba untuk menyediakan antarmuka yang sama.

Pool.applyseperti Python apply, kecuali bahwa pemanggilan fungsi dilakukan dalam proses terpisah. Pool.applyblok sampai fungsi selesai.

Pool.apply_asyncjuga seperti built-in Python apply, kecuali bahwa panggilan kembali dengan segera alih-alih menunggu hasilnya. Sebuah AsyncResultobjek dikembalikan. Anda memanggil get()metode untuk mengambil hasil dari panggilan fungsi. The get()Metode blok sampai fungsi selesai. Jadi, pool.apply(func, args, kwargs)sama dengan pool.apply_async(func, args, kwargs).get().

Berbeda dengan Pool.apply, Pool.apply_asyncmetode ini juga memiliki panggilan balik yang, jika disediakan, dipanggil ketika fungsi selesai. Ini bisa digunakan alih-alih menelepon get().

Sebagai contoh:

import multiprocessing as mp
import time

def foo_pool(x):
    time.sleep(2)
    return x*x

result_list = []
def log_result(result):
    # This is called whenever foo_pool(i) returns a result.
    # result_list is modified only by the main process, not the pool workers.
    result_list.append(result)

def apply_async_with_callback():
    pool = mp.Pool()
    for i in range(10):
        pool.apply_async(foo_pool, args = (i, ), callback = log_result)
    pool.close()
    pool.join()
    print(result_list)

if __name__ == '__main__':
    apply_async_with_callback()

dapat menghasilkan hasil seperti

[1, 0, 4, 9, 25, 16, 49, 36, 81, 64]

Perhatikan, tidak seperti pool.map, urutan hasil mungkin tidak sesuai dengan urutan di mana pool.apply_asyncpanggilan dilakukan.


Jadi, jika Anda perlu menjalankan fungsi dalam proses terpisah, tetapi ingin proses saat ini untuk memblokir sampai fungsi itu kembali, gunakan Pool.apply. Seperti Pool.apply, Pool.mapmemblokir sampai hasil lengkap dikembalikan.

Jika Anda ingin Kelompok pekerja proses melakukan banyak panggilan fungsi secara tidak sinkron, gunakan Pool.apply_async. The agar hasil tidak dijamin sama dengan urutan panggilan ke Pool.apply_async.

Perhatikan juga bahwa Anda dapat memanggil sejumlah fungsi berbedaPool.apply_async (tidak semua panggilan perlu menggunakan fungsi yang sama).

Sebaliknya, Pool.mapberlaku fungsi yang sama untuk banyak argumen. Namun, tidak seperti Pool.apply_async, hasilnya dikembalikan dalam urutan yang sesuai dengan urutan argumen.

unutbu
sumber
11
Haruskah ada if __name__=="__main__"sebelumnya apply_async_with_callback()di Windows?
jfs
3
Terima kasih banyak. bagaimana dengan map_async?
Phyo Arkar Lwin
38
Lihat di dalam multiprocessing / pool.py dan Anda akan melihat bahwa Pool.map(func,iterable)itu setara dengan Pool.map_async(func,iterable).get(). Jadi hubungan antara Pool.mapdan Pool.map_asyncmirip dengan Pool.applydan Pool.apply_async. The asyncperintah kembali segera, sedangkan non asyncperintah memblokir. The asyncperintah juga memiliki callback.
unutbu
7
Memutuskan antara menggunakan Pool.mapdan Pool.applymirip dengan memutuskan kapan akan menggunakan mapatau applydengan Python. Anda cukup menggunakan alat yang sesuai dengan pekerjaan. Memutuskan antara menggunakan asyncdan bukan asyncversi tergantung pada apakah Anda ingin panggilan untuk memblokir proses saat ini dan / atau jika Anda ingin menggunakan panggilan balik.
unutbu
6
@falsePockets: Ya. Setiap panggilan untuk apply_asyncmengembalikan ApplyResultobjek. Menyebut bahwa ApplyResult's getmetode akan mengembalikan nilai kembali fungsi terkait (atau kenaikan gaji mp.TimeoutErrorjika kali-out. Panggilan) Jadi, jika Anda menempatkan ApplyResultdalam daftar memerintahkan, kemudian memanggil mereka getmetode akan mengembalikan hasil dalam urutan yang sama. Anda bisa menggunakannya pool.mapdalam situasi ini.
unutbu
75

Tentang applyvs map:

pool.apply(f, args): fhanya dieksekusi di SALAH SATU pekerja kolam. Jadi SALAH SATU proses di kolam akan berjalan f(args).

pool.map(f, iterable): Metode ini memotong iterable ke dalam sejumlah chunk yang dikirimkan ke kumpulan proses sebagai tugas yang terpisah. Jadi, Anda memanfaatkan semua proses di kolam renang.

kakhkAtion
sumber
4
bagaimana jika iterable adalah generator
RustyShackleford
Hmm ... Pertanyaan bagus. Sejujurnya saya belum pernah menggunakan kolam dengan generator, tetapi utas ini mungkin bisa membantu: stackoverflow.com/questions/5318936/…
kakhkAtion
@kakhkAtion Mengenai berlaku, jika hanya satu pekerja yang menjalankan fungsinya, apa yang dilakukan pekerja lainnya? Apakah saya harus menelepon melamar beberapa kali agar pekerja lain melakukan tugas?
Moondra
3
Benar. Juga lihat pool.apply_async jika Anda ingin makan siang pekerja secara tidak sinkron. "pool_apply mencekal hingga hasilnya siap, jadi apply_async () lebih cocok untuk melakukan pekerjaan secara paralel"
kakhkAtion
1
Apa yang terjadi ketika saya memiliki 4 proses tetapi telah memanggil apply_async()8 kali? Apakah akan secara otomatis menanganinya dengan antrian?
Saravanabalagi Ramachandran
31

Berikut ini adalah ikhtisar dalam format tabel untuk menunjukkan perbedaan antara Pool.apply, Pool.apply_async, Pool.mapdan Pool.map_async. Saat memilih satu, Anda harus mempertimbangkan multi-argumen, konkurensi, pemblokiran, dan pemesanan:

                  | Multi-args   Concurrence    Blocking     Ordered-results
---------------------------------------------------------------------
Pool.map          | no           yes            yes          yes
Pool.map_async    | no           yes            no           yes
Pool.apply        | yes          no             yes          no
Pool.apply_async  | yes          yes            no           no
Pool.starmap      | yes          yes            yes          yes
Pool.starmap_async| yes          yes            no           no

Catatan:

  • Pool.imapdan Pool.imap_async- versi peta dan map_async yang lebih malas.

  • Pool.starmap Metode, sangat mirip dengan metode peta selain itu menerima beberapa argumen.

  • Asyncmetode mengirimkan semua proses sekaligus dan mengambil hasilnya setelah selesai. Gunakan metode get untuk mendapatkan hasil.

  • Pool.map(atau Pool.apply) metode sangat mirip dengan peta bawaan Python (atau terapkan). Mereka memblokir proses utama sampai semua proses selesai dan mengembalikan hasilnya.

Contoh:

peta

Disebut untuk daftar pekerjaan dalam satu waktu

results = pool.map(func, [1, 2, 3])

menerapkan

Hanya bisa dipanggil untuk satu pekerjaan

for x, y in [[1, 1], [2, 2]]:
    results.append(pool.apply(func, (x, y)))

def collect_result(result):
    results.append(result)

map_async

Disebut untuk daftar pekerjaan dalam satu waktu

pool.map_async(func, jobs, callback=collect_result)

apply_async

Hanya dapat dipanggil untuk satu pekerjaan dan mengeksekusi pekerjaan di latar belakang secara paralel

for x, y in [[1, 1], [2, 2]]:
    pool.apply_async(worker, (x, y), callback=collect_result)

starmap

Merupakan varian pool.mapyang mendukung banyak argumen

pool.starmap(func, [(1, 1), (2, 1), (3, 1)])

starmap_async

Kombinasi starmap () dan map_async () yang beriterasi iterable dari iterables dan memanggil func dengan iterables dibongkar. Mengembalikan objek hasil.

pool.starmap_async(calculate_worker, [(1, 1), (2, 1), (3, 1)], callback=collect_result)

Referensi:

Temukan dokumentasi lengkap di sini: https://docs.python.org/3/library/multiprocessing.html

Rene B.
sumber
2
Pool.starmap () sedang memblokir
Alan Evangelista