Apakah Python mendukung multithreading? Bisakah itu mempercepat waktu eksekusi?

95

Saya sedikit bingung apakah multithreading berfungsi dengan Python atau tidak.

Saya tahu ada banyak pertanyaan tentang ini dan saya telah membaca banyak di antaranya, tetapi saya masih bingung. Saya tahu dari pengalaman saya sendiri dan telah melihat orang lain memposting jawaban dan contoh mereka sendiri di sini di StackOverflow bahwa multithreading memang dimungkinkan dengan Python. Jadi mengapa semua orang terus mengatakan bahwa Python dikunci oleh GIL dan hanya satu utas yang dapat berjalan dalam satu waktu? Ini jelas berhasil. Atau apakah ada perbedaan yang tidak saya dapatkan di sini?

Banyak poster / responden juga tetap menyebutkan bahwa threading dibatasi karena tidak menggunakan banyak core. Tapi saya akan mengatakan mereka masih berguna karena mereka melakukan pekerjaan secara bersamaan dan dengan demikian menyelesaikan beban kerja gabungan lebih cepat. Maksud saya, mengapa bahkan ada modul utas Python?

Memperbarui:

Terima kasih atas semua jawaban sejauh ini. Cara saya memahaminya adalah bahwa multithreading hanya akan berjalan secara paralel untuk beberapa tugas IO, tetapi hanya dapat berjalan satu per satu untuk beberapa tugas inti yang terikat CPU.

Saya tidak sepenuhnya yakin apa artinya ini bagi saya dalam istilah praktis, jadi saya hanya akan memberikan contoh jenis tugas yang ingin saya multithread. Misalnya, saya ingin mengulang melalui daftar string yang sangat panjang dan saya ingin melakukan beberapa operasi string dasar pada setiap item daftar. Jika saya memisahkan daftar, mengirim setiap sublist untuk diproses oleh kode loop / string saya di thread baru, dan mengirim hasilnya kembali ke antrean, apakah beban kerja ini akan berjalan kira-kira pada waktu yang sama? Yang paling penting apakah ini secara teoritis akan mempercepat waktu yang diperlukan untuk menjalankan skrip?

Contoh lain mungkin jika saya dapat membuat dan menyimpan empat gambar berbeda menggunakan PIL di empat utas berbeda, dan apakah ini lebih cepat daripada memproses gambar satu per satu demi satu? Saya kira komponen kecepatan inilah yang benar-benar saya pertanyakan daripada terminologi yang benar.

Saya juga tahu tentang modul multiprosesing tetapi minat utama saya saat ini adalah untuk beban tugas kecil-menengah (10-30 detik) dan jadi menurut saya multithreading akan lebih sesuai karena subproses bisa lambat untuk dimulai.

Karim Bahgat
sumber
4
Ini adalah pertanyaan yang cukup berat. Saya pikir jawabannya terletak pada apa yang Anda ingin agar utas lakukan. Dalam kebanyakan situasi, GIL mencegah lebih dari 1 utas berjalan secara bersamaan. Namun, ada beberapa kasus dimana GIL dirilis (misalnya membaca dari file) sehingga bisa dilakukan secara paralel. Perhatikan juga bahwa GIL adalah detail implementasi Cpython (implementasi paling umum). Tidak ada implementasi lain dari python (Jython, PyPy, dll) yang memiliki GIL (AFAIK)
mgilson
2
@mgilson PyPy memiliki GIL.
2
@ Delnan - Anda tampaknya benar. Terima kasih.
mgilson
1
"subproses bisa lambat untuk dimulai" - Anda dapat membuat kumpulan tugas yang siap dijalankan. Overhead dapat dibatasi pada kira-kira jumlah waktu yang diperlukan untuk membuat serial / deserialisasi data yang diperlukan agar tugas mulai bekerja.
Brian Cain
1
@KariBahgat, itulah yang saya maksud.
Brian Cain

Jawaban:

132

GIL tidak mencegah penguliran. Semua yang dilakukan GIL adalah memastikan hanya satu utas yang menjalankan kode Python pada satu waktu; kontrol masih beralih di antara utas.

Apa yang GIL cegah kemudian, adalah menggunakan lebih dari satu inti CPU atau CPU terpisah untuk menjalankan utas secara paralel.

Ini hanya berlaku untuk kode Python. Ekstensi C dapat dan memang merilis GIL untuk memungkinkan beberapa utas kode C dan satu utas Python dijalankan di beberapa inti. Ini meluas ke I / O yang dikendalikan oleh kernel, seperti select()panggilan untuk membaca dan menulis soket, membuat Python menangani peristiwa jaringan secara cukup efisien dalam pengaturan multi-inti multi-utas.

Apa yang kemudian dilakukan oleh banyak penerapan server, adalah menjalankan lebih dari satu proses Python, agar OS menangani penjadwalan antar proses untuk memanfaatkan inti CPU Anda secara maksimal. Anda juga dapat menggunakan multiprocessingpustaka untuk menangani pemrosesan paralel di beberapa proses dari satu basis kode dan proses induk, jika itu sesuai dengan kasus penggunaan Anda.

Perhatikan bahwa GIL hanya berlaku untuk implementasi CPython; Jython dan IronPython menggunakan implementasi threading yang berbeda (masing-masing VM Java asli dan thread runtime umum .NET).

Untuk mengatasi pembaruan Anda secara langsung: Setiap tugas yang mencoba untuk mendapatkan peningkatan kecepatan dari eksekusi paralel, menggunakan kode Python murni, tidak akan melihat percepatan karena kode Python berulir dikunci ke satu utas yang dieksekusi pada satu waktu. Jika Anda mencampur ekstensi C dan I / O, bagaimanapun (seperti operasi PIL atau numpy) dan kode C apa pun dapat berjalan secara paralel dengan satu utas Python aktif.

Python threading sangat bagus untuk membuat GUI yang responsif, atau untuk menangani beberapa permintaan web singkat di mana I / O lebih menjadi penghambat daripada kode Python. Ini tidak cocok untuk memparalelkan kode Python intensif komputasi, tetap berpegang pada multiprocessingmodul untuk tugas-tugas semacam itu atau mendelegasikan ke perpustakaan eksternal khusus.

Martijn Pieters
sumber
Terima kasih @MartijnPieters, maka saya memiliki jawaban yang lebih jelas untuk pertanyaan saya apakah threading dapat digunakan untuk mempercepat kode seperti for-loop, yaitu "tidak". Mungkin Anda atau seseorang dapat menulis jawaban baru yang dapat saya terima yang memberikan beberapa contoh spesifik dari modul / kode / operasi umum di mana threading akan diizinkan oleh GIL untuk menjalankan paralell dan dengan demikian lebih cepat (misalnya contoh dari I / O dan jaringan / operasi pembacaan soket yang telah disebutkan, dan kasus lain di mana multithreading dengan Python berguna). Mungkin daftar bagus penggunaan multithread umum dan beberapa contoh pemrograman jika memungkinkan?
Karim Bahgat
4
Tidak, menurut saya jawaban seperti itu tidak akan sangat membantu; Sejujurnya. Anda tidak dapat membuat daftar lengkap, selamanya, tetapi aturan praktisnya adalah bahwa setiap I / O (membaca dan menulis file, soket jaringan, pipa) ditangani di C, dan banyak pustaka C juga merilis GIL untuk mereka. operasi, tetapi terserah perpustakaan untuk mendokumentasikan ini untuk Anda.
Martijn Pieters
1
Saya buruk, tidak melihat jawaban Anda yang diperbarui sampai sekarang, di mana Anda memberikan beberapa contoh penggunaan utas yang bagus. Ini termasuk (perbaiki saya jika saya salah) pemrograman jaringan (misalnya urllib.urlopen()?), Untuk memanggil satu skrip Python dari dalam Python GUI, dan memanggil beberapa operasi PIL (misalnya Image.transform()) dan numpy (misalnya numpy.array()) dengan utas. Dan Anda memberikan beberapa contoh lagi dalam komentar Anda seperti menggunakan beberapa utas untuk membaca file (mis. f.read()?). Saya tahu daftar lengkap tidak mungkin, hanya ingin jenis contoh yang Anda berikan dalam pembaruan Anda. Bagaimanapun, terima jawaban Anda :)
Karim Bahgat
2
@KarimBahgat: Ya, urllib.urlopen()akan memanggil soket jaringan, menunggu soket I / O adalah kesempatan bagus untuk mengganti utas dan melakukan hal lain.
Martijn Pieters
4
Meskipun tidak secara langsung relevan dengan masalah ini, perlu dicatat bahwa terkadang threading sama sekali bukan tentang kinerja; mungkin lebih mudah untuk menulis kode Anda sebagai beberapa utas eksekusi independen. Misalnya, Anda mungkin memiliki satu utas memutar musik latar, satu melayani UI, dan satu menenggelamkan pada komputasi yang harus dilakukan pada akhirnya tetapi tidak terburu-buru. Mencoba mengurutkan pemutaran audio buffer berikutnya dengan runloop UI, atau memecah komputasi Anda menjadi bagian-bagian yang cukup kecil agar tidak mengganggu interaktivitas, mungkin jauh lebih sulit daripada menggunakan utas.
abarnert
4

Iya. :)

Anda memiliki modul ulir tingkat rendah dan modul penguliran tingkat yang lebih tinggi . Tetapi jika Anda hanya ingin menggunakan mesin multicore, modul multiproses adalah cara yang tepat.

Kutipan dari dokumen :

Di CPython, karena Global Interpreter Lock, hanya satu utas yang dapat mengeksekusi kode Python sekaligus (meskipun pustaka berorientasi kinerja tertentu mungkin mengatasi batasan ini). Jika Anda ingin aplikasi Anda memanfaatkan sumber daya komputasi mesin multi-core dengan lebih baik, Anda disarankan untuk menggunakan multiprocessing. Namun, threading masih merupakan model yang sesuai jika Anda ingin menjalankan beberapa tugas terikat I / O secara bersamaan.

zord
sumber
3

Threading Diizinkan dengan Python, satu-satunya masalah adalah GIL akan memastikan bahwa hanya satu utas yang dijalankan pada satu waktu (tidak ada paralelisme).

Jadi pada dasarnya jika Anda ingin membuat multi-utas kode untuk mempercepat penghitungan, itu tidak akan mempercepatnya karena hanya satu utas yang dijalankan pada satu waktu, tetapi jika Anda menggunakannya untuk berinteraksi dengan database misalnya, itu akan.

r.guerbab
sumber