Di Python secara khusus, bagaimana variabel dibagikan di antara utas?
Meskipun saya telah menggunakan threading.Thread
sebelumnya, saya tidak pernah benar-benar memahami atau melihat contoh bagaimana variabel dibagikan. Apakah mereka terbagi antara utas utama dan anak-anak atau hanya di antara anak-anak? Kapan saya perlu menggunakan penyimpanan lokal utas untuk menghindari pembagian ini?
Saya telah melihat banyak peringatan tentang sinkronisasi akses ke data bersama di antara utas dengan menggunakan kunci tetapi saya belum melihat contoh masalah yang benar-benar bagus.
Terima kasih sebelumnya!
Jawaban:
Di Python, semuanya dibagikan, kecuali untuk variabel lokal-fungsi (karena setiap pemanggilan fungsi mendapatkan kumpulan lokal sendiri, dan utas selalu merupakan pemanggilan fungsi yang terpisah.) Dan bahkan kemudian, hanya variabel itu sendiri (nama yang merujuk ke objek) bersifat lokal untuk fungsi tersebut; objek itu sendiri selalu global, dan apa pun bisa merujuknya. The
Thread
objek untuk thread tertentu bukan objek khusus dalam hal ini. Jika Anda menyimpanThread
objek di suatu tempat yang dapat diakses semua thread (seperti variabel global), semua thread dapat mengakses satuThread
objek tersebut. Jika Anda ingin mengubah apa pun yang dapat diakses oleh utas lain secara menyeluruh, Anda harus melindunginya dengan kunci. Dan tentu saja semua utas harus berbagi kunci yang sama ini, atau itu tidak akan efektif.Jika Anda menginginkan penyimpanan lokal-utas yang sebenarnya, di situlah
threading.local
masuk. Atributthreading.local
tidak dibagi di antara utas; setiap utas hanya melihat atribut yang ditempatkan di sana. Jika Anda ingin tahu tentang implementasinya, sumbernya ada di _threading_local.py di pustaka standar.sumber
Perhatikan kode berikut:
Di sini threading.local () digunakan sebagai cara cepat dan kotor untuk meneruskan beberapa data dari run () ke bar () tanpa mengubah antarmuka foo ().
Perhatikan bahwa menggunakan variabel global tidak akan berhasil:
Sementara itu, jika Anda mampu meneruskan data ini sebagai argumen foo () - itu akan menjadi cara yang lebih elegan dan dirancang dengan baik:
Tetapi ini tidak selalu memungkinkan saat menggunakan kode pihak ketiga atau yang dirancang dengan buruk.
sumber
Anda dapat membuat penyimpanan lokal utas menggunakan
threading.local()
.Data yang disimpan ke tls akan menjadi unik untuk setiap utas yang akan membantu memastikan bahwa pembagian yang tidak disengaja tidak terjadi.
sumber
Sama seperti di setiap bahasa lain, setiap utas Python memiliki akses ke variabel yang sama. Tidak ada perbedaan antara 'utas utama' dan utas turunan.
Satu perbedaan dengan Python adalah Global Interpreter Lock berarti hanya satu utas yang dapat menjalankan kode Python pada satu waktu. Namun, ini tidak banyak membantu dalam hal sinkronisasi akses, karena semua masalah pre-emption biasa masih berlaku, dan Anda harus menggunakan primitif threading seperti dalam bahasa lain. Ini berarti Anda perlu mempertimbangkan kembali jika Anda menggunakan utas untuk kinerja, namun.
sumber
Saya mungkin salah di sini. Jika Anda tahu sebaliknya, harap jelaskan karena ini akan membantu menjelaskan mengapa seseorang perlu menggunakan thread local ().
Pernyataan ini sepertinya tidak tepat, tidak salah: "Jika Anda ingin memodifikasi secara menyeluruh apa pun yang dapat diakses oleh utas lain, Anda harus melindunginya dengan kunci." Menurut saya pernyataan ini -> efektif <- benar tetapi tidak sepenuhnya akurat. Saya pikir istilah "atom" berarti bahwa penerjemah Python membuat potongan kode byte yang tidak menyisakan ruang untuk sinyal interupsi ke CPU.
Saya pikir operasi atom adalah potongan kode byte Python yang tidak memberikan akses ke interupsi. Pernyataan Python seperti "running = True" adalah atom. Anda tidak perlu mengunci CPU dari interupsi dalam kasus ini (saya yakin). Rincian kode byte Python aman dari gangguan utas.
Kode Python seperti "threads_running [5] = True" tidak bersifat atomik. Ada dua potongan kode byte Python di sini; satu untuk de-referensi list () untuk objek dan potongan kode byte lainnya untuk menetapkan nilai ke objek, dalam hal ini "tempat" dalam daftar. Sebuah interupsi dapat dimunculkan -> antara <- dua byte-code -> potongan <-. Itu adalah hal buruk yang terjadi.
Bagaimana thread local () berhubungan dengan "atomic"? Inilah sebabnya mengapa pernyataan itu tampaknya salah arah bagi saya. Jika tidak, bisakah Anda menjelaskan?
sumber