Apa sebenarnya yang Dilakukan Metode .join () Modul multiprosesing Python?

110

Belajar tentang Python Multiprocessing (dari artikel PMOTW ) dan akan menyukai beberapa klarifikasi tentang apa sebenarnya yang dilakukan join()metode ini.

Dalam tutorial lama dari tahun 2008 disebutkan bahwa tanpa p.join()panggilan dalam kode di bawah ini, "proses anak akan diam dan tidak berhenti, menjadi zombie yang harus Anda bunuh secara manual".

from multiprocessing import Process

def say_hello(name='world'):
    print "Hello, %s" % name

p = Process(target=say_hello)
p.start()
p.join()

Saya menambahkan cetakan dari PIDserta a time.sleepuntuk diuji dan sejauh yang saya tahu, prosesnya berakhir dengan sendirinya:

from multiprocessing import Process
import sys
import time

def say_hello(name='world'):
    print "Hello, %s" % name
    print 'Starting:', p.name, p.pid
    sys.stdout.flush()
    print 'Exiting :', p.name, p.pid
    sys.stdout.flush()
    time.sleep(20)

p = Process(target=say_hello)
p.start()
# no p.join()

dalam 20 detik:

936 ttys000    0:00.05 /Library/Frameworks/Python.framework/Versions/2.7/Reso
938 ttys000    0:00.00 /Library/Frameworks/Python.framework/Versions/2.7/Reso
947 ttys001    0:00.13 -bash

setelah 20 detik:

947 ttys001    0:00.13 -bash

Perilakunya sama dengan p.join()menambahkan kembali di akhir file. Python Module of the Week menawarkan penjelasan modul yang sangat mudah dibaca ; "Untuk menunggu hingga proses menyelesaikan pekerjaannya dan keluar, gunakan metode join ().", Tetapi tampaknya setidaknya OS X melakukannya.

Saya juga bertanya-tanya tentang nama metode tersebut. Apakah .join()metode ini menggabungkan sesuatu di sini? Apakah itu menggabungkan proses dengan akhirnya? Atau apakah itu hanya berbagi nama dengan bahasa asli Python.join() metode ?

MikeiLL
sumber
2
Sejauh yang saya tahu, itu memegang utas utama dan menunggu proses anak selesai dan kemudian bergabung kembali sumber daya di utas utama, sebagian besar melakukan keluar bersih.
abhishekgarg
ah itu masuk akal. Jadi sebenarnya CPU, Memory resourcesdipisahkan dari proses induk, lalu joindiedit kembali setelah proses anak selesai?
MikeiLL
ya, itulah yang dilakukannya. Jadi, jika Anda tidak bergabung dengan mereka kembali, ketika proses anak selesai itu hanya akan menjadi proses yang mati atau mati
abhishekgarg
@abishekarg Itu tidak benar. Proses anak akan digabungkan secara implisit saat proses utama selesai.
dano
@dano, saya juga belajar python dan saya baru saja membagikan apa yang saya temukan dalam tes saya, dalam tes saya saya memiliki proses utama yang tidak pernah berakhir jadi mungkin itu sebabnya saya melihat proses anak itu mati.
abhishekgarg

Jawaban:

125

The join()metode, bila digunakan dengan threadingatau multiprocessing, tidak berhubungan dengan str.join()- itu tidak benar-benar concatenating apapun bersama-sama. Sebaliknya, ini hanya berarti "tunggu [utas / proses] ini selesai". Nama joinini digunakan karena multiprocessingAPI modul dimaksudkan agar terlihat serupa dengan threadingAPI modul, dan threadingmodul digunakan joinuntuk Threadobjeknya. Menggunakan istilahjoin berarti "menunggu utas selesai" adalah umum di banyak bahasa pemrograman, jadi Python juga mengadopsinya.

Sekarang, alasan Anda melihat penundaan 20 detik dengan dan tanpa panggilan ke join()adalah karena secara default, saat proses utama siap untuk keluar, secara implisit akan memanggil join()semua multiprocessing.Processinstance yang berjalan . Ini tidak dinyatakan dengan jelas di multiprocessingdokumen sebagaimana mestinya, tetapi disebutkan di bagian Panduan Pemrograman :

Ingat juga bahwa proses non-daemonik akan digabungkan secara otomatis.

Anda dapat mengganti perilaku ini dengan menyetel daemonbendera pada Processmenjadi Truesebelum memulai proses:

p = Process(target=say_hello)
p.daemon = True
p.start()
# Both parent and child will exit here, since the main process has completed.

Jika Anda melakukannya, proses turunan akan dihentikan segera setelah proses utama selesai :

daemon

Bendera daemon proses, nilai Boolean. Ini harus disetel sebelum start () dipanggil.

Nilai awal diwarisi dari proses pembuatan.

Ketika sebuah proses keluar, ia mencoba untuk menghentikan semua proses anak daemoniknya.

dano
sumber
6
Saya memahami p.daemon=Trueitu untuk "memulai proses latar belakang yang berjalan tanpa memblokir program utama keluar". Tetapi jika "Proses daemon dihentikan secara otomatis sebelum program utama keluar", apa sebenarnya kegunaannya?
MikeiLL
8
@MikeiLL Pada dasarnya apa pun yang Anda inginkan terjadi di latar belakang selama proses induk berjalan, tetapi itu tidak perlu dibersihkan dengan baik sebelum keluar dari program utama. Mungkin proses pekerja yang membaca data dari soket atau perangkat keras, dan memberikan data itu kembali ke induk melalui antrian atau memprosesnya di latar belakang untuk beberapa tujuan? Secara umum saya akan mengatakan bahwa menggunakan daemonicproses anak tidak terlalu aman, karena proses akan dihentikan tanpa mengizinkan untuk membersihkan sumber daya terbuka yang mungkin dimilikinya .. (lanjutan).
dano
7
@MikeiLL Praktik yang lebih baik adalah memberi isyarat kepada anak untuk membersihkan dan keluar sebelum keluar dari proses utama. Anda mungkin berpikir akan masuk akal untuk membiarkan proses anak daemonik berjalan saat induknya keluar, tetapi perlu diingat bahwa multiprocessingAPI dirancang untuk meniru threadingAPI sedekat mungkin. threading.ThreadObjek daemonik dihentikan segera setelah utas utama keluar, sehingga multiprocesing.Processobjek daemonik berperilaku dengan cara yang sama.
dano
38

Tanpa join() , proses utama dapat selesai sebelum proses anak selesai. Saya tidak yakin dalam keadaan apa yang menyebabkan zombieisme.

Tujuan utama join() adalah untuk memastikan bahwa proses anak telah selesai sebelum proses utama melakukan apa pun yang bergantung pada pekerjaan proses anak.

Etimologi dari join()adalah kebalikan dari fork, yang merupakan istilah umum dalam sistem operasi keluarga Unix untuk membuat proses anak. Sebuah proses tunggal "bercabang" menjadi beberapa, lalu "bergabung" kembali menjadi satu.

Russell Borogove
sumber
2
Ia menggunakan nama join()karena join()itulah yang digunakan untuk menunggu threading.Threadobjek selesai, dan multiprocessingAPI dimaksudkan untuk meniru threadingAPI sebanyak mungkin.
dano
Pernyataan kedua Anda membahas masalah yang saya hadapi dalam proyek saat ini.
MikeiLL
Saya mengerti bagian di mana utas utama menunggu sub-proses selesai, tetapi bukankah hal itu mengalahkan tujuan eksekusi Asynchronous? Bukankah itu seharusnya menyelesaikan eksekusi, secara mandiri (sub-tugas atau proses)?
Apurva Kunkulol
1
@ApurvaKunkulol Bergantung pada cara Anda menggunakannya, tetapi join()diperlukan dalam kasus di mana utas utama memerlukan hasil pekerjaan sub-utas. Misalnya, jika Anda merender sesuatu dan menetapkan 1/4 gambar akhir ke masing-masing dari 4 subproses, dan ingin menampilkan seluruh gambar setelah selesai.
Russell Borogove
@RussellBorogove Ah! Saya mengerti. Maka arti dari aktivitas Asynchronous sedikit berbeda disini. Ini hanya berarti fakta bahwa sub-proses dimaksudkan untuk menjalankan tugas mereka secara bersamaan dengan utas utama sementara utas utama juga melakukan tugasnya alih-alih hanya menunggu sub-proses.
Apurva Kunkulol
12

Saya tidak akan menjelaskan secara rinci apa yang joindilakukannya, tetapi inilah etimologi dan intuisi di baliknya, yang akan membantu Anda mengingat maknanya dengan lebih mudah.

Idenya adalah bahwa eksekusi " bercabang " menjadi beberapa proses yang salah satunya adalah master, pekerja lainnya (atau "budak"). Ketika pekerja selesai, mereka "bergabung" dengan master sehingga eksekusi serial dapat dilanjutkan.

The joinMetode menyebabkan proses master untuk menunggu seorang pekerja untuk bergabung. Metode ini mungkin lebih baik disebut "tunggu", karena itulah perilaku sebenarnya yang ditimbulkannya di master (dan itulah yang disebut dalam POSIX, meskipun utas POSIX juga menyebutnya "bergabung"). Bergabung hanya terjadi sebagai akibat dari benang bekerjasama dengan baik, itu bukan sesuatu master tidak .

Nama "fork" dan "join" telah digunakan dengan arti ini dalam multiprocessing sejak 1963 .

larsmans
sumber
Jadi dengan cara penggunaan kata ini joinmungkin telah mendahului penggunaannya dalam merujuk pada penggabungan, sebagai lawan dari sebaliknya.
MikeiLL
1
Tidak mungkin bahwa penggunaan dalam penggabungan berasal dari penggunaan dalam multiprocessing; namun kedua pengertian tersebut diturunkan secara terpisah dari arti kata tersebut dalam bahasa Inggris.
Russell Borogove
2

join()digunakan untuk menunggu proses pekerja keluar. Seseorang harus menelepon close()atau terminate()sebelum menggunakan join().

Seperti @Russell yang disebutkan, join seperti kebalikan dari fork (yang memunculkan sub-proses).

Untuk bergabung untuk menjalankan Anda harus menjalankan close()yang akan mencegah lebih banyak tugas dikirim ke kolam dan keluar setelah semua tugas selesai. Alternatifnya, menjalankan terminate()hanya akan keluar dengan menghentikan semua proses pekerja segera.

"the child process will sit idle and not terminate, becoming a zombie you must manually kill" Hal ini dimungkinkan ketika proses utama (induk) keluar tetapi proses anak masih berjalan dan setelah selesai tidak ada proses induk untuk mengembalikan status keluarnya.

Ani Menon
sumber
2

The join()panggilan memastikan bahwa baris berikutnya dari kode tidak disebut sebelum semua proses multiprocessing selesai.

Misalnya, tanpa join(), kode berikut akan memanggil restart_program()bahkan sebelum proses selesai, yang mirip dengan asynchronous dan bukan yang kita inginkan (Anda dapat mencoba):

num_processes = 5

for i in range(num_processes):
    p = multiprocessing.Process(target=calculate_stuff, args=(i,))
    p.start()
    processes.append(p)
for p in processes:
    p.join() # call to ensure subsequent line (e.g. restart_program) 
             # is not called until all processes finish

restart_program()
Yi Xiang Chong
sumber
0

Untuk menunggu sampai proses menyelesaikan pekerjaannya dan keluar, gunakan metode join ().

dan

Catatan Penting untuk menggabungkan () proses setelah menghentikannya untuk memberikan waktu mesin latar belakang untuk memperbarui status objek untuk mencerminkan penghentian.

Ini adalah contoh yang baik membantu saya memahaminya: di sini

Satu hal yang saya perhatikan secara pribadi adalah proses utama saya dihentikan sementara sampai anak menyelesaikan prosesnya menggunakan metode join () yang mengalahkan poin yang saya gunakan multiprocessing.Process()di tempat pertama.

Josh
sumber