Mengapa Kunci Penerjemah Global?

89

Apa sebenarnya fungsi Kunci Interpreter Global Python? Apakah bahasa lain yang dikompilasi ke bytecode menggunakan mekanisme serupa?

Federico A. Ramponi
sumber
6
Anda juga harus bertanya "Apakah itu penting?"
S. Lott
2
Saya setuju, saya menganggapnya bukan masalah sekarang karena di 2.6 modul multiprosesing telah ditambahkan untuk memungkinkan Anda memprogram menggunakan banyak proses dengan cara seperti thread. docs.python.org/library/multiprocessing.html
monkut
Apa Gil itu: stackoverflow.com/questions/1294382/… Terkait dengan Programmer: softwareengineering.stackexchange.com/questions/186889/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Jawaban:

69

Secara umum, untuk masalah keamanan utas apa pun, Anda perlu melindungi struktur data internal dengan kunci. Ini dapat dilakukan dengan berbagai tingkat perincian.

  • Anda dapat menggunakan penguncian halus, di mana setiap struktur terpisah memiliki kuncinya sendiri.

  • Anda dapat menggunakan penguncian berbutir kasar di mana satu kunci melindungi semuanya (pendekatan GIL).

Ada berbagai pro dan kontra dari setiap metode. Penguncian mendetail memungkinkan paralelisme yang lebih besar - dua utas dapat dieksekusi secara paralel saat tidak berbagi sumber daya apa pun. Namun ada biaya administrasi yang jauh lebih besar. Untuk setiap baris kode, Anda mungkin perlu memperoleh dan melepaskan beberapa kunci.

Pendekatan berbutir kasar adalah kebalikannya. Dua utas tidak dapat berjalan pada saat yang sama, tetapi utas individu akan berjalan lebih cepat karena tidak melakukan banyak pembukuan. Pada akhirnya itu bermuara pada pertukaran antara kecepatan single-threaded dan paralelisme.

Ada beberapa upaya untuk menghapus GIL dengan python, tetapi biaya tambahan untuk mesin berulir tunggal umumnya terlalu besar. Beberapa kasus sebenarnya bisa lebih lambat bahkan pada mesin multi-prosesor karena pertentangan kunci.

Apakah bahasa lain yang dikompilasi ke bytecode menggunakan mekanisme serupa?

Ini bervariasi, dan mungkin tidak boleh dianggap sebagai properti bahasa sebanyak properti implementasi. Misalnya, ada implementasi Python seperti Jython dan IronPython yang menggunakan pendekatan threading dari VM yang mendasarinya, bukan pendekatan GIL. Selain itu, Ruby versi berikutnya tampaknya bergerak menuju pengenalan GIL.

Brian
sumber
1
dapatkah Anda menjelaskan ini: 'Dua utas tidak dapat berjalan pada waktu yang sama'? Baru-baru ini saya menulis webserver sederhana dengan Python dengan multithreading. Untuk setiap permintaan baru dari klien, server mengeluarkan utas baru untuk itu dan utas itu terus dijalankan. Jadi akan ada beberapa utas yang berjalan pada saat bersamaan bukan? Atau apakah saya mengerti dengan cara yang salah?
avi
1
Utas python @avi AFAIK tidak dapat berjalan secara bersamaan, tetapi itu tidak berarti satu utas harus memblokir utas lainnya. GIL hanya berarti bahwa hanya satu utas yang dapat menafsirkan kode python pada satu waktu, itu tidak berarti bahwa manajemen utas dan alokasi sumber daya tidak berfungsi.
Benproductions1
2
^ jadi kapan saja, hanya satu utas yang akan menyajikan konten ke klien ... jadi tidak ada gunanya benar-benar menggunakan multithreading untuk meningkatkan kinerja. Baik?
avi
Dan, tentu saja, Java dikompilasi menjadi kode byte dan memungkinkan penguncian yang sangat halus.
Warren Dew
3
@avi, proses terikat IO seperti server web masih dapat memperoleh dari utas Python. Dua atau lebih utas dapat melakukan IO secara bersamaan. Mereka tidak bisa diinterpretasikan (CPU) secara bersamaan.
Saish
33

Berikut ini adalah dari Manual Referensi API Python / C resmi :

Penerjemah Python tidak sepenuhnya aman untuk thread. Untuk mendukung program multi-utas Python, ada kunci global yang harus dipegang oleh utas saat ini sebelum dapat mengakses objek Python dengan aman. Tanpa kunci, bahkan operasi yang paling sederhana pun dapat menyebabkan masalah dalam program multi-thread: misalnya, ketika dua thread secara bersamaan menambah jumlah referensi dari objek yang sama, jumlah referensi bisa bertambah hanya sekali, bukan dua kali.

Oleh karena itu, ada aturan bahwa hanya thread yang telah memperoleh kunci interpreter global yang dapat beroperasi pada objek Python atau memanggil fungsi Python / C API. Untuk mendukung program Python multi-utas, penerjemah secara teratur melepaskan dan memperoleh kembali kunci - secara default, setiap 100 instruksi bytecode (ini dapat diubah dengan sys.setcheckinterval ()). Kunci juga dilepaskan dan diperoleh kembali di sekitar operasi I / O yang berpotensi memblokir seperti membaca atau menulis file, sehingga utas lain dapat berjalan sementara utas yang meminta I / O sedang menunggu operasi I / O selesai.

Saya pikir ini merangkum masalah dengan cukup baik.

Eli Bendersky
sumber
1
Saya membacanya juga, tetapi saya tidak dapat memahami mengapa Python berbeda dalam hal ini dari, katakanlah, java (apakah itu?)
Federico A. Ramponi
@EliBendersky Python utas diimplementasikan sebagai pthread dan ditangani oleh OS ( dabeaz.com/python/UnderstandingGIL.pdf ) sedangkan utas Java adalah utas tingkat aplikasi yang penjadwalannya ditangani oleh JVM
gokul_uf
19

Kunci penerjemah global adalah kunci tipe mutex besar yang melindungi penghitung referensi agar tidak disemprot. Jika Anda menulis kode python murni, ini semua terjadi di belakang layar, tetapi jika Anda menyematkan Python ke dalam C, Anda mungkin harus secara eksplisit mengambil / melepaskan kuncinya.

Mekanisme ini tidak terkait dengan Python yang dikompilasi ke bytecode. Ini tidak diperlukan untuk Java. Bahkan, itu bahkan tidak diperlukan untuk Jython (python dikompilasi ke jvm).

lihat juga pertanyaan ini

David Nehme
sumber
4
"Mekanisme ini tidak terkait dengan Python yang dikompilasi ke bytecode": Tepatnya, ini adalah artefak dari implementasi CPython. Implementasi lain (seperti Jython yang telah Anda sebutkan) dapat terbebas dari pembatasan ini berdasarkan implementasi thread-safe mereka
Eli Bendersky
11

Python, seperti perl 5, tidak dirancang dari awal agar aman untuk benang. Utas dicangkokkan setelah fakta, sehingga kunci penerjemah global digunakan untuk menjaga pengecualian timbal balik di mana hanya satu utas yang mengeksekusi kode pada waktu tertentu di perut penerjemah.

Utas Python individu secara kooperatif melakukan banyak tugas oleh penerjemah itu sendiri dengan memutar kunci sesering mungkin.

Mengambil kunci sendiri diperlukan ketika Anda berbicara dengan Python dari C ketika utas Python lain aktif untuk 'ikut serta' ke protokol ini dan memastikan tidak ada yang tidak aman terjadi di belakang Anda.

Sistem lain yang memiliki warisan single-threaded yang kemudian berkembang menjadi sistem mulithread sering memiliki mekanisme semacam ini. Misalnya, kernel Linux memiliki "Big Kernel Lock" dari masa awal SMP. Secara bertahap seiring waktu karena kinerja multi-threading menjadi masalah, ada kecenderungan untuk mencoba memecah jenis penguncian ini menjadi bagian-bagian yang lebih kecil atau menggantinya dengan algoritme bebas kunci dan struktur data jika memungkinkan untuk memaksimalkan throughput.

Edward KMETT
sumber
1 untuk menyebutkan fakta bahwa penguncian berbutir kasar digunakan daripada yang dipikirkan kebanyakan orang, terutama BKL yang sering dilupakan (saya menggunakan reiserfs- satu-satunya alasan sebenarnya yang saya tahu sama sekali).
baru123456
3
Linux memiliki BKL, sejak versi 2.6.39, BKL telah dihapus seluruhnya.
avi
5
Tentu saja. Ingatlah itu ~ 3 tahun setelah saya menjawab pertanyaan itu. =)
Edward KMETT
7

Mengenai pertanyaan kedua Anda, tidak semua bahasa skrip menggunakan ini, tetapi itu hanya membuatnya kurang kuat. Misalnya, thread di Ruby berwarna hijau dan bukan native.

Dalam Python, utas adalah asli dan GIL hanya mencegahnya berjalan pada inti yang berbeda.

Di Perl, utasnya bahkan lebih buruk. Mereka hanya menyalin seluruh interpreter, dan jauh dari dapat digunakan seperti di Python.

Eli Bendersky
sumber
2

Mungkin ini artikel oleh BDFL akan membantu.

Jeremy Cantrell
sumber