Saya perlu antrian tempat beberapa utas dapat memasukkan berbagai hal, dan beberapa utas dapat dibaca.
Python memiliki setidaknya dua kelas antrian, Antrian. Antrian dan collections.deque, dengan yang pertama tampaknya menggunakan yang terakhir secara internal. Keduanya mengklaim aman dalam dokumentasi.
Namun, dokumen Antrian juga menyatakan:
collections.deque adalah implementasi alternatif dari antrian tidak terbatas dengan operasi append atom cepat () dan popleft () yang tidak memerlukan penguncian.
Yang saya kira saya tidak mengerti: Apakah ini berarti deque tidak sepenuhnya aman setelah semua?
Jika ya, saya mungkin tidak sepenuhnya memahami perbedaan antara kedua kelas. Saya dapat melihat bahwa Antrian menambahkan fungsi pemblokiran. Di sisi lain, ia kehilangan beberapa fitur deque seperti dukungan untuk operator.
Mengakses objek deque internal secara langsung, adalah
x dalam Antrian () .queque
aman?
Juga, mengapa Antrian menggunakan mutex untuk operasi itu ketika deque sudah thread-safe?
sumber
RuntimeError: deque mutated during iteration
adalah apa yang bisa Anda dapatkan adalah menggunakan shareddeque
antara beberapa utas dan tidak ada penguncian ...deque
saat iterasi bahkan di utas yang sama. Satu-satunya alasan Anda tidak bisa mendapatkan kesalahan iniQueue
adalah karenaQueue
tidak mendukung iterasi.Jawaban:
Queue.Queue
dancollections.deque
melayani berbagai tujuan. Queue.Queue dimaksudkan untuk memungkinkan utas berbeda berkomunikasi menggunakan pesan / data yang antri, sedangkancollections.deque
hanya dimaksudkan sebagai struktur data. Itu sebabnyaQueue.Queue
memiliki metode sepertiput_nowait()
,get_nowait()
, danjoin()
, sedangkancollections.deque
tidak.Queue.Queue
tidak dimaksudkan untuk digunakan sebagai koleksi, karena itu ia tidak memiliki sukain
operator.Intinya begini: jika Anda memiliki banyak utas dan Anda ingin mereka dapat berkomunikasi tanpa perlu kunci, Anda sedang mencari
Queue.Queue
; jika Anda hanya ingin antrian atau antrian ganda sebagai datastructure, gunakancollections.deque
.Akhirnya, mengakses dan memanipulasi deque internal
Queue.Queue
bermain dengan api - Anda benar-benar tidak ingin melakukan itu.sumber
Queue.Queue
, ia menggunakan dideque
bawah tenda.collections.deque
adalah koleksi, sedangkanQueue.Queue
mekanisme komunikasi. OverheadQueue.Queue
adalah untuk membuatnya threadsafe. Menggunakandeque
untuk berkomunikasi di antara utas hanya akan menyebabkan balapan yang menyakitkan. Setiap kalideque
ada threadsafe, itu adalah kecelakaan bahagia tentang bagaimana penerjemah diimplementasikan, dan bukan sesuatu yang bisa diandalkan. Itu sebabnyaQueue.Queue
ada di tempat pertama.deque is threadsafe by accident due to the existence of GIL
; memang benar bahwadeque
bergantung pada GIL untuk memastikan keamanan benang - tetapi tidakby accident
. Dokumentasi python resmi dengan jelas menyatakan bahwadeque
pop*
/append*
metode aman-thread. Jadi setiap implementasi python yang valid harus memberikan jaminan yang sama (implementasi GIL-less harus mencari tahu bagaimana melakukannya tanpa GIL). Anda dapat dengan aman mengandalkan jaminan itu.deque
untuk komunikasi. Jika Anda membungkusnyapop
dengantry/except
, Anda akan berakhir dengan loop sibuk memakan sejumlah besar CPU hanya menunggu data baru. Ini tampak seperti pendekatan yang sangat tidak efisien dibandingkan dengan panggilan pemblokiran yang ditawarkan olehQueue
, yang memastikan bahwa utas menunggu data akan tertidur dan tidak membuang waktu CPU.Queue.Queue
saat itu, karena itu ditulis menggunakancollections.deque
: hg.python.org/cpython/file/2.7/Lib/Queue.py - menggunakan variabel kondisi untuk secara efisien memungkinkandeque
membungkusnya diakses melewati batas utas dengan aman dan efisien. Penjelasan tentang bagaimana Anda akan menggunakandeque
untuk komunikasi ada di sana di sumber.Jika semua yang Anda cari adalah cara aman untuk mentransfer objek di antara utas , maka keduanya akan berfungsi (baik untuk FIFO dan LIFO). Untuk FIFO:
Queue.put()
danQueue.get()
aman untuk benangdeque.append()
dandeque.popleft()
aman untuk benangcatatan:
deque
mungkin tidak aman, saya tidak yakin.deque
tidak memblokirpop()
ataupopleft()
jadi Anda tidak dapat mendasarkan alur utas konsumen Anda pada pemblokiran sampai item baru tiba.Namun, tampaknya deque memiliki keunggulan efisiensi yang signifikan . Berikut adalah beberapa hasil benchmark dalam hitungan detik menggunakan CPython 2.7.3 untuk menyisipkan dan menghapus item 100k
Inilah kode patokan:
sumber
deque
mungkin bukan thread yang aman". Dari mana Anda mendapatkannya?Untuk informasi, ada tiket Python yang dirujuk untuk keamanan thread deque ( https://bugs.python.org/issue15329 ). Judul "memperjelas metode deque mana yang aman untuk thread"
Intinya di sini: https://bugs.python.org/issue15329#msg199368
Lagi pula, jika Anda tidak 100% yakin dan Anda lebih suka keandalan daripada kinerja, cukup cantumkan Kunci seperti;)
sumber
Semua metode elemen tunggal di
deque
atomik dan aman. Semua metode lain juga aman untuk thread. Hal-hal sepertilen(dq)
,dq[4]
menghasilkan nilai yang benar sesaat. Tapi pikirkan misalnya tentangdq.extend(mylist)
: Anda tidak mendapatkan jaminan bahwa semua elemen masukmylist
diajukan secara berurutan ketika utas lainnya juga menambahkan elemen di sisi yang sama - tetapi itu biasanya bukan persyaratan dalam komunikasi antar-utas dan untuk tugas yang dipertanyakan.Jadi a
deque
~ 20x lebih cepat daripadaQueue
(yang menggunakandeque
under the hood) dan kecuali Anda tidak memerlukan API sinkronisasi "nyaman" (pemblokiran / batas waktu),maxsize
kepatuhan yang ketat atau "Abaikan metode ini (_put, _get, .. ) untuk mengimplementasikan organisasi lain " perilaku sub-klasifikasi antrian , atau ketika Anda mengurus hal-hal seperti itu sendiri, maka telanjangdeque
adalah kesepakatan yang baik dan efisien untuk komunikasi antar-jalur berkecepatan tinggi.Kenyataannya, penggunaan metode mutex ekstra dan metode ekstra
._get()
dll. Yang banyak dilakukanQueue.py
adalah karena kendala kompatibilitas ke belakang, desain yang berlebihan di masa lalu, dan kurangnya perawatan untuk memberikan solusi yang efisien untuk masalah bottleneck kecepatan penting ini dalam komunikasi antar-benang. Daftar digunakan dalam versi Python yang lebih lama - tetapi bahkan list.append () /. Pop (0) adalah & adalah atomic dan threadsafe ...sumber
Menambahkan
notify_all()
ke masingdeque
append
- masing danpopleft
menghasilkan hasil yang jauh lebih burukdeque
daripada peningkatan 20x yang dicapai olehdeque
perilaku default :@ Jonathan sedikit memodifikasi kodenya dan saya mendapatkan benchmark menggunakan cPython 3.6.2 dan menambahkan kondisi dalam deque loop untuk mensimulasikan perilaku yang dilakukan Queue.
Dan sepertinya kinerja dibatasi oleh fungsi ini
condition.notify_all()
sumber
deque
aman dari benang. "operasi yang tidak memerlukan penguncian" berarti bahwa Anda tidak harus melakukan penguncian sendiri,deque
mengurusnya.Mengambil melihat pada
Queue
sumber, deque internal disebutself.queue
dan menggunakan mutex untuk accesor dan mutasi, sehinggaQueue().queue
adalah tidak benang-aman untuk digunakan.Jika Anda mencari operator "in", maka deque atau antrian mungkin bukan struktur data yang paling tepat untuk masalah Anda.
sumber
(sepertinya saya tidak memiliki reputasi untuk berkomentar ...) Anda harus berhati-hati metode deque mana yang Anda gunakan dari utas yang berbeda.
deque.get () tampaknya threadsafe, tetapi saya telah menemukan bahwa melakukan
dapat gagal jika utas lainnya menambahkan item pada saat yang sama. Saya mendapat RuntimeException yang mengeluh "deque bermutasi selama iterasi".
Periksa collectionsmodule.c untuk melihat operasi mana yang dipengaruhi oleh ini
sumber
>>> di = {1:None} >>> for x in di: del di[x]
while
loop.