Saya mencoba untuk menggunakan multiprocessing
's Pool.map()
fungsi untuk membagi pekerjaan secara bersamaan. Ketika saya menggunakan kode berikut, itu berfungsi dengan baik:
import multiprocessing
def f(x):
return x*x
def go():
pool = multiprocessing.Pool(processes=4)
print pool.map(f, range(10))
if __name__== '__main__' :
go()
Namun, ketika saya menggunakannya dalam pendekatan yang lebih berorientasi objek, itu tidak berhasil. Pesan kesalahan yang diberikannya adalah:
PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup
__builtin__.instancemethod failed
Ini terjadi ketika berikut ini adalah program utama saya:
import someClass
if __name__== '__main__' :
sc = someClass.someClass()
sc.go()
dan berikut ini adalah someClass
kelas saya :
import multiprocessing
class someClass(object):
def __init__(self):
pass
def f(self, x):
return x*x
def go(self):
pool = multiprocessing.Pool(processes=4)
print pool.map(self.f, range(10))
Adakah yang tahu apa masalahnya, atau cara yang mudah untuk menyelesaikannya?
python
multithreading
multiprocessing
pickle
pool
ventolin
sumber
sumber
PicklingError: Can't pickle <class 'function'>: attribute lookup builtins.function failed
Jawaban:
Masalahnya adalah bahwa multiprocessing harus mengambil hal-hal untuk sling mereka di antara proses, dan metode terikat tidak dapat dipilih. Solusinya (apakah Anda menganggapnya "mudah" atau tidak ;-) adalah menambahkan infrastruktur ke program Anda untuk memungkinkan metode seperti itu untuk diacar , mendaftarkannya dengan metode library standar copy_reg .
Sebagai contoh, kontribusi Steven Bethard pada utas ini (menjelang akhir utas) menunjukkan satu pendekatan yang bisa diterapkan dengan baik untuk memungkinkan metode pengawetan / pembongkaran via
copy_reg
.sumber
_pickle_method
Pengembalian Andaself._unpickle_method
, metode terikat; jadi tentu saja acar sekarang mencoba untuk acar ITU - dan itu sesuai dengan yang Anda katakan: dengan menelepon_pickle_method
, secara rekursif. Yaitu denganOO
memasukkan kode dengan cara ini, Anda pasti telah memperkenalkan rekursi tak terbatas. Saya sarankan kembali ke kode Steven (dan tidak beribadah di altar OO ketika tidak tepat: banyak hal dengan Python paling baik dilakukan dengan cara yang lebih fungsional, dan ini satu).Semua solusi ini jelek karena multiprosesing dan pengawetan rusak dan terbatas kecuali Anda melompat di luar perpustakaan standar.
Jika Anda menggunakan fork yang
multiprocessing
dipanggilpathos.multiprocesssing
, Anda bisa langsung menggunakan kelas dan metode kelas dalammap
fungsi multi-pemrosesan . Ini karenadill
digunakan sebagai gantipickle
ataucPickle
, dandill
dapat membuat serialisasi hampir semua hal dengan python.pathos.multiprocessing
juga menyediakan fungsi peta yang tidak sinkron ... dan dapatmap
berfungsi dengan banyak argumen (mis.map(math.pow, [1,2,3], [4,5,6])
)Lihat: Apa yang dapat dilakukan multiprocessing dan dill bersama?
dan: http://matthewrocklin.com/blog/work/2013/12/05/Parallelism-and-Serialization/
Dan hanya untuk menjadi eksplisit, Anda dapat melakukan persis apa yang ingin Anda lakukan di tempat pertama, dan Anda dapat melakukannya dari penerjemah, jika Anda mau.
Dapatkan kode di sini: https://github.com/uqfoundation/pathos
sumber
pathos
penulisnya. Versi yang Anda maksud sudah berumur beberapa tahun. Coba versi di github, Anda dapat menggunakanpathos.pp
atau github.com/uqfoundation/ppft .pip install setuptools
begitupip install git+https://github.com/uqfoundation/pathos.git@master
. Ini akan mendapatkan dependensi yang sesuai. Rilis baru hampir siap ... sekarang hampir semuanyapathos
juga berjalan di windows, dan3.x
kompatibel.Anda juga bisa mendefinisikan
__call__()
metode di dalam AndasomeClass()
, yang memanggilsomeClass.go()
dan kemudian mengirimkan instancesomeClass()
ke pool. Objek ini acar dan berfungsi baik (untuk saya) ...sumber
__call__()
? Saya pikir jawaban Anda mungkin yang lebih bersih - saya berjuang untuk memahami kesalahan ini, dan pertama kali saya datang untuk melihat panggilan. Ngomong-ngomong, jawaban ini juga membantu menjelaskan apa yang multiprocessing lakukan: [ stackoverflow.com/a/20789937/305883]Namun beberapa keterbatasan terhadap solusi Steven Bethard:
Ketika Anda mendaftarkan metode kelas Anda sebagai suatu fungsi, penghancur kelas Anda secara mengejutkan dipanggil setiap kali pemrosesan metode Anda selesai. Jadi, jika Anda memiliki 1 instance dari kelas Anda yang memanggil n kali metodenya, anggota dapat menghilang antara 2 run dan Anda mungkin mendapatkan pesan
malloc: *** error for object 0x...: pointer being freed was not allocated
(misalnya file anggota terbuka) ataupure virtual method called, terminate called without an active exception
(yang berarti dari masa pakai objek anggota yang saya gunakan lebih pendek dari apa yang saya pikirkan). Saya mendapatkan ini ketika berhadapan dengan n lebih besar dari ukuran kolam. Ini adalah contoh singkat:Keluaran:
The
__call__
Metode tidak begitu setara, karena [None, ...] dibaca dari hasil:Jadi tidak satupun dari kedua metode ini memuaskan ...
sumber
None
kembali karena definisi Anda__call__
hilangreturn
: seharusnyareturn self.process_obj(i)
.Ada jalan pintas lain yang bisa Anda gunakan, meskipun bisa jadi tidak efisien tergantung pada apa yang ada di instance kelas Anda.
Seperti yang dikatakan semua orang, masalahnya adalah bahwa
multiprocessing
kode tersebut harus mengacak hal-hal yang dikirimkannya ke sub-proses yang telah dimulai, dan pemetik tidak melakukan metode contoh.Namun, alih-alih mengirim metode-instance, Anda dapat mengirim instance kelas aktual, ditambah nama fungsi yang akan dipanggil, ke fungsi biasa yang kemudian digunakan
getattr
untuk memanggil metode-instance, sehingga menciptakan metode terikat dalamPool
subproses. Ini mirip dengan mendefinisikan__call__
metode kecuali bahwa Anda dapat memanggil lebih dari satu fungsi anggota.Mencuri kode @ EricH. Dari jawabannya dan sedikit membubuhi keterangan (saya mengetik ulang karenanya semua nama berubah dan semacamnya, untuk beberapa alasan ini tampak lebih mudah daripada cut-and-paste :-)) untuk ilustrasi semua keajaiban:
Keluaran menunjukkan bahwa, memang, konstruktor dipanggil sekali (dalam pid asli) dan destruktor disebut 9 kali (sekali untuk setiap salinan dibuat = 2 atau 3 kali per kumpulan-pekerja-proses sesuai kebutuhan, ditambah sekali dalam aslinya proses). Ini sering OK, seperti dalam kasus ini, karena pemetik default membuat salinan dari seluruh instance dan (semi-) secara diam-diam mengisinya kembali — dalam hal ini, melakukan:
—Karena itu meskipun destruktor disebut delapan kali dalam tiga proses pekerja, ia menghitung mundur dari 1 menjadi 0 setiap kali — tetapi tentu saja Anda masih dapat mendapat masalah dengan cara ini. Jika perlu, Anda bisa menyediakan sendiri
__setstate__
:dalam hal ini misalnya.
sumber
Anda juga bisa mendefinisikan
__call__()
metode di dalam AndasomeClass()
, yang memanggilsomeClass.go()
dan kemudian mengirimkan instancesomeClass()
ke pool. Objek ini acar dan berfungsi baik (untuk saya) ...sumber
Solusi dari parisjohn di atas berfungsi baik dengan saya. Ditambah lagi kode terlihat bersih dan mudah dimengerti. Dalam kasus saya ada beberapa fungsi untuk memanggil menggunakan Pool, jadi saya memodifikasi kode parisjohn sedikit di bawah ini. Saya membuat panggilan untuk dapat memanggil beberapa fungsi, dan nama fungsi diteruskan dalam argumen dari
go()
:sumber
Solusi yang mungkin sepele untuk ini adalah beralih ke menggunakan
multiprocessing.dummy
. Ini adalah implementasi berbasis antarmuka multiprocessing yang tampaknya tidak memiliki masalah ini di Python 2.7. Saya tidak punya banyak pengalaman di sini, tetapi perubahan impor cepat ini memungkinkan saya untuk memanggil apply_async pada metode kelas.Beberapa sumber yang bagus tentang
multiprocessing.dummy
:https://docs.python.org/2/library/multiprocessing.html#module-multiprocessing.dummy
http://chriskiehl.com/article/parallelism-in-one-line/
sumber
Dalam kasus sederhana ini, di mana
someClass.f
tidak mewarisi data apa pun dari kelas dan tidak melampirkan apa pun ke kelas, solusi yang mungkin adalah dengan memisahkanf
, sehingga dapat diambil acar:sumber
Mengapa tidak menggunakan func terpisah?
sumber
Saya mengalami masalah yang sama tetapi menemukan bahwa ada JSON encoder yang dapat digunakan untuk memindahkan objek-objek ini antara proses.
Gunakan ini untuk membuat daftar Anda:
Kemudian dalam fungsi yang dipetakan, gunakan ini untuk memulihkan objek:
sumber
Pembaruan: pada hari penulisan ini, namedTuples dapat dipilih (dimulai dengan python 2.7)
Masalahnya di sini adalah proses anak tidak dapat mengimpor kelas objek -dalam kasus ini, kelas P-, dalam kasus proyek multi-model Kelas P harus dapat diimpor di mana saja proses anak digunakan
solusi cepat adalah membuatnya dapat diimpor dengan memengaruhinya ke global ()
sumber