Saya perhatikan bahwa sering disarankan untuk menggunakan antrian dengan banyak utas, bukan daftar dan .pop()
. Apakah ini karena daftar tidak aman untuk alasan lain, atau karena alasan lain?
155
Saya perhatikan bahwa sering disarankan untuk menggunakan antrian dengan banyak utas, bukan daftar dan .pop()
. Apakah ini karena daftar tidak aman untuk alasan lain, atau karena alasan lain?
Jawaban:
Daftar itu sendiri aman untuk thread. Dalam CPython, GIL melindungi terhadap akses bersamaan untuk mereka, dan implementasi lainnya berhati-hati untuk menggunakan kunci berbutir halus atau tipe data yang disinkronkan untuk implementasi daftar mereka. Namun, sementara daftar sendiri tidak bisa pergi korup dengan upaya untuk akses bersamaan, daftar ini data yang tidak dilindungi. Sebagai contoh:
tidak dijamin untuk benar-benar meningkatkan L [0] oleh satu jika thread lain melakukan hal yang sama, karena
+=
bukan merupakan operasi atom. (Sangat, sangat sedikit operasi di Python sebenarnya atom, karena sebagian besar dari mereka dapat menyebabkan kode Python sewenang-wenang disebut.) Anda harus menggunakan Antrian karena jika Anda hanya menggunakan daftar yang tidak dilindungi, Anda mungkin mendapatkan atau menghapus item yang salah karena ras kondisi.sumber
Untuk memperjelas titik dalam jawaban Thomas' baik, harus disebutkan bahwa
append()
adalah thread aman.Ini karena tidak ada kekhawatiran bahwa data yang sedang dibaca akan berada di tempat yang sama setelah kita menulisnya . The
append()
operasi tidak membaca data, hanya menulis data ke dalam daftar.sumber
PyList_Append
dilakukan dalam satu kunci GIL. Itu diberikan referensi ke objek untuk ditambahkan. Konten objek itu dapat diubah setelah dievaluasi dan sebelum panggilan kePyList_Append
dilakukan. Tapi itu akan tetap menjadi objek yang sama, dan ditambahkan dengan aman (jika Anda melakukannyalst.append(x); ok = lst[-1] is x
, makaok
mungkin False, tentu saja). Kode yang Anda referensi tidak membaca dari objek yang ditambahkan, kecuali untuk INCREF. Bunyinya, dan mungkin merealokasi, daftar yang ditambahkan.L[0] += x
akan melakukan__getitem__
onL
dan kemudian__setitem__
onL
- jikaL
mendukungnya__iadd__
akan melakukan sesuatu yang sedikit berbeda pada antarmuka objek, tetapi masih ada dua operasi terpisah padaL
level interpreter python (Anda akan melihatnya di dikompilasi dengan bytecode). Halappend
ini dilakukan dalam pemanggilan metode tunggal dalam bytecode.remove
?Berikut adalah daftar lengkap belum lengkap non-contoh dari
list
operasi dan apakah atau tidak mereka thread aman. Berharap mendapatkan jawaban mengenaiobj in a_list
konstruk bahasa di sini .sumber
Saya baru-baru ini memiliki kasus ini di mana saya perlu menambahkan daftar terus-menerus dalam satu utas, loop melalui item dan memeriksa apakah item sudah siap, itu adalah AsyncResult dalam kasus saya dan menghapusnya dari daftar hanya jika sudah siap. Saya tidak dapat menemukan contoh yang menunjukkan masalah saya dengan jelas. Berikut adalah contoh yang menunjukkan penambahan ke daftar di satu utas secara terus-menerus dan menghapus dari daftar yang sama di utas lain terus-menerus beberapa kali dan Anda akan melihat kesalahan
Versi cacat
Output saat KESALAHAN
Versi yang menggunakan kunci
Keluaran
Kesimpulan
Seperti disebutkan dalam jawaban sebelumnya sementara tindakan menambahkan atau muncul elemen dari daftar itu sendiri adalah thread aman, yang tidak aman thread adalah ketika Anda menambahkan satu thread dan pop di yang lain
sumber
with r:
) daripada menelepon secara eksplisitr.acquire()
danr.release()