Manufaktur industri
Industri Internet of Things | bahan industri | Pemeliharaan dan Perbaikan Peralatan | Pemrograman industri |
home  MfgRobots >> Manufaktur industri >  >> Industrial programming >> Python

Multithreading dengan Python dengan Contoh:Belajar GIL dengan Python

Bahasa pemrograman python memungkinkan Anda untuk menggunakan multiprocessing atau multithreading. Dalam tutorial ini, Anda akan belajar cara menulis aplikasi multithread dengan Python.

Apa itu Utas?

Utas adalah unit eksekusi pada pemrograman bersamaan. Multithreading adalah teknik yang memungkinkan CPU untuk mengeksekusi banyak tugas dari satu proses pada waktu yang sama. Utas ini dapat dijalankan satu per satu sambil membagikan sumber daya prosesnya.

Apa itu Proses?

Sebuah proses pada dasarnya adalah program yang sedang dieksekusi. Saat Anda memulai aplikasi di komputer Anda (seperti browser atau editor teks), sistem operasi membuat proses .

Apa itu Multithreading dengan Python?

Multithreading dengan Python pemrograman adalah teknik terkenal di mana banyak utas dalam suatu proses berbagi ruang data mereka dengan utas utama yang membuat berbagi informasi dan komunikasi dalam utas menjadi mudah dan efisien. Utas lebih ringan dari proses. Multi utas dapat dijalankan secara individual sambil berbagi sumber daya prosesnya. Tujuan dari multithreading adalah untuk menjalankan banyak tugas dan sel fungsi secara bersamaan.

Dalam tutorial ini, Anda akan belajar,

Apa itu Multiprosesing?

Multiprocessing memungkinkan Anda untuk menjalankan beberapa proses yang tidak terkait secara bersamaan. Proses ini tidak berbagi sumber daya dan berkomunikasi melalui IPC.

Python Multithreading vs Multiprocessing

Untuk memahami proses dan utas, pertimbangkan skenario ini:Berkas .exe di komputer Anda adalah sebuah program. Saat Anda membukanya, OS memuatnya ke dalam memori, dan CPU menjalankannya. Contoh program yang sedang berjalan disebut proses.

Setiap proses akan memiliki 2 komponen mendasar:

Sekarang, sebuah proses dapat berisi satu atau lebih sub-bagian yang disebut utas. Ini tergantung pada arsitektur OS. Anda dapat menganggap thread sebagai bagian dari proses yang dapat dijalankan secara terpisah oleh sistem operasi.

Dengan kata lain, ini adalah aliran instruksi yang dapat dijalankan secara independen oleh OS. Utas dalam satu proses berbagi data dari proses tersebut dan dirancang untuk bekerja sama guna memfasilitasi paralelisme.

Mengapa menggunakan Multithreading?

Multithreading memungkinkan Anda untuk memecah aplikasi menjadi beberapa sub-tugas dan menjalankan tugas-tugas ini secara bersamaan. Jika Anda menggunakan multithreading dengan benar, kecepatan, kinerja, dan rendering aplikasi Anda dapat ditingkatkan.

Python MultiThreading

Python mendukung konstruksi untuk multiprocessing serta multithreading. Dalam tutorial ini, Anda terutama akan berfokus pada penerapan multithreaded aplikasi dengan python. Ada dua modul utama yang dapat digunakan untuk menangani thread dengan Python:

  1. Utas modul, dan
  2. Utasan modul

Namun, dalam python, ada juga sesuatu yang disebut kunci juru bahasa global (GIL). Itu tidak memungkinkan peningkatan kinerja yang besar dan bahkan dapat mengurangi kinerja beberapa aplikasi multithreaded. Anda akan mempelajari semuanya di bagian berikutnya dari tutorial ini.

Modul Thread dan Threading

Dua modul yang akan Anda pelajari dalam tutorial ini adalah modul thread dan modul threading .

Namun, modul utas sudah lama tidak digunakan lagi. Dimulai dengan Python 3, telah ditetapkan sebagai usang dan hanya dapat diakses sebagai __thread untuk kompatibilitas mundur.

Anda harus menggunakan utas tingkat yang lebih tinggi modul untuk aplikasi yang ingin Anda terapkan. Modul utas hanya dibahas di sini untuk tujuan pendidikan.

Modul Utas

Sintaks untuk membuat thread baru menggunakan modul ini adalah sebagai berikut:

thread.start_new_thread(function_name, arguments)

Baiklah, sekarang Anda telah membahas teori dasar untuk memulai coding. Jadi, buka IDLE atau notepad Anda dan ketik berikut ini:

import time
import _thread

def thread_test(name, wait):
   i = 0
   while i <= 3:
      time.sleep(wait)
      print("Running %s\n" %name)
      i = i + 1

   print("%s has finished execution" %name)

if __name__ == "__main__":
    
    _thread.start_new_thread(thread_test, ("First Thread", 1))
    _thread.start_new_thread(thread_test, ("Second Thread", 2))
    _thread.start_new_thread(thread_test, ("Third Thread", 3))


Simpan file dan tekan F5 untuk menjalankan program. Jika semuanya dilakukan dengan benar, ini adalah output yang akan Anda lihat:

Anda akan mempelajari lebih lanjut tentang kondisi balapan dan cara menanganinya di bagian mendatang

KODE PENJELASAN

  1. Pernyataan ini mengimpor modul waktu dan utas yang digunakan untuk menangani eksekusi dan penundaan utas Python.
  2. Di sini, Anda telah mendefinisikan fungsi yang disebut thread_test, yang akan dipanggil oleh start_new_thread metode. Fungsi menjalankan loop sementara untuk empat iterasi dan mencetak nama utas yang memanggilnya. Setelah iterasi selesai, ia mencetak pesan yang mengatakan bahwa utas telah selesai dieksekusi.
  3. Ini adalah bagian utama dari program Anda. Di sini, Anda cukup memanggil start_new_thread metode dengan thread_test berfungsi sebagai argumen. Ini akan membuat utas baru untuk fungsi yang Anda berikan sebagai argumen dan mulai menjalankannya. Perhatikan bahwa Anda dapat mengganti ini (utas_ test) dengan fungsi lain yang ingin Anda jalankan sebagai utas.

Modul Ulir

Modul ini adalah implementasi tingkat tinggi dari threading di python dan standar de facto untuk mengelola aplikasi multithreaded. Ini menyediakan berbagai fitur jika dibandingkan dengan modul thread.

Berikut adalah daftar beberapa fungsi berguna yang didefinisikan dalam modul ini:

Nama Fungsi Deskripsi
activeCount() Mengembalikan jumlah Utas benda yang masih hidup
currentThread() Mengembalikan objek kelas Thread saat ini.
menghitung() Mencantumkan semua objek Thread yang aktif.
isDaemon() Mengembalikan nilai true jika utasnya adalah daemon.
isAlive() Mengembalikan nilai true jika utasnya masih hidup.
Metode Kelas Utas
mulai() Memulai aktivitas thread. Itu harus dipanggil hanya sekali untuk setiap utas karena akan menimbulkan kesalahan runtime jika dipanggil beberapa kali.
lari() Metode ini menunjukkan aktivitas thread dan dapat ditimpa oleh kelas yang memperluas kelas Thread.
bergabung() Ini memblokir eksekusi kode lain hingga utas di mana metode join() dipanggil dihentikan.

Latar Belakang:Kelas Thread

Sebelum Anda mulai coding program multithreaded menggunakan modul threading, penting untuk memahami tentang kelas Thread. Kelas thread adalah kelas utama yang mendefinisikan template dan operasi thread di python.

Cara paling umum untuk membuat aplikasi python multithread adalah dengan mendeklarasikan kelas yang memperluas kelas Thread dan mengganti metode run()-nya.

Kelas Thread, secara ringkas, menandakan urutan kode yang berjalan di utas separate yang terpisah kendali.

Jadi, saat menulis aplikasi multithread, Anda akan melakukan hal berikut:

  1. mendefinisikan kelas yang memperluas kelas Thread
  2. Ganti __init__ konstruktor
  3. Mengganti run() metode

Setelah objek utas dibuat, start() metode dapat digunakan untuk memulai eksekusi aktivitas ini dan join() metode dapat digunakan untuk memblokir semua kode lain hingga aktivitas saat ini selesai.

Sekarang, mari kita coba menggunakan modul threading untuk mengimplementasikan contoh sebelumnya. Sekali lagi, jalankan IDLE Anda dan ketik berikut ini:

import time
import threading

class threadtester (threading.Thread):
    def __init__(self, id, name, i):
       threading.Thread.__init__(self)
       self.id = id
       self.name = name
       self.i = i
       
    def run(self):
       thread_test(self.name, self.i, 5)
       print ("%s has finished execution " %self.name)

def thread_test(name, wait, i):

    while i:
       time.sleep(wait)
       print ("Running %s \n" %name)
       i = i - 1

if __name__=="__main__":
    thread1 = threadtester(1, "First Thread", 1)
    thread2 = threadtester(2, "Second Thread", 2)
    thread3 = threadtester(3, "Third Thread", 3)

    thread1.start()
    thread2.start()
    thread3.start()

    thread1.join()
    thread2.join()
    thread3.join()

Ini akan menjadi output ketika Anda menjalankan kode di atas:

KODE PENJELASAN

  1. Bagian ini sama dengan contoh kita sebelumnya. Di sini, Anda mengimpor modul waktu dan utas yang digunakan untuk menangani eksekusi dan penundaan utas Python.
  2. Dalam bit ini, Anda membuat kelas yang disebut threadtester, yang mewarisi atau memperluas Thread kelas modul threading. Ini adalah salah satu cara paling umum untuk membuat utas dengan python. Namun, Anda hanya boleh mengganti konstruktor dan run() metode di aplikasi Anda. Seperti yang Anda lihat pada contoh kode di atas, __init__ metode (konstruktor) telah diganti. Demikian pula, Anda juga telah mengganti run() metode. Ini berisi kode yang ingin Anda jalankan di dalam utas. Dalam contoh ini, Anda telah memanggil fungsi thread_test().
  3. Ini adalah metode thread_test() yang mengambil nilai i sebagai argumen, kurangi 1 pada setiap iterasi dan perulangan melalui sisa kode hingga i menjadi 0. Dalam setiap iterasi, ia mencetak nama utas yang sedang dieksekusi dan tidur selama beberapa detik (yang juga dianggap sebagai argumen ).
  4. thread1 =threadtester(1, “First Thread”, 1) Di sini, kita membuat thread dan meneruskan tiga parameter yang kita deklarasikan di __init__. Parameter pertama adalah id utas, parameter kedua adalah nama utas, dan parameter ketiga adalah penghitung, yang menentukan berapa kali perulangan while harus dijalankan.
  5. thread2.start()T metode start digunakan untuk memulai eksekusi sebuah thread. Secara internal, fungsi start() memanggil metode run() kelas Anda.
  6. thread3.join() Metode join() memblokir eksekusi kode lain dan menunggu hingga thread yang dipanggil selesai.

Seperti yang sudah Anda ketahui, utas yang berada dalam proses yang sama memiliki akses ke memori dan data dari proses itu. Akibatnya, jika lebih dari satu utas mencoba mengubah atau mengakses data secara bersamaan, kesalahan mungkin muncul.

Di bagian berikutnya, Anda akan melihat berbagai jenis komplikasi yang dapat muncul saat thread mengakses data dan critical-section tanpa memeriksa transaksi akses yang ada.

Deadlock dan kondisi Balapan

Sebelum mempelajari tentang deadlock dan kondisi balapan, akan sangat membantu untuk memahami beberapa definisi dasar yang terkait dengan pemrograman bersamaan:

  • Bagian KritisIni adalah fragmen kode yang mengakses atau memodifikasi variabel bersama dan harus dilakukan sebagai transaksi atomik.
  • Context SwitchIni adalah proses yang diikuti CPU untuk menyimpan status thread sebelum berpindah dari satu tugas ke tugas lainnya sehingga dapat dilanjutkan dari titik yang sama nanti.

Deadlock

Kebuntuan adalah masalah yang paling ditakuti yang dihadapi pengembang saat menulis aplikasi bersamaan/multithreaded dengan python. Cara terbaik untuk memahami kebuntuan adalah dengan menggunakan contoh masalah ilmu komputer klasik yang dikenal sebagai Masalah Filsuf Makan.

Rumusan masalah untuk filosof makan adalah sebagai berikut:

Lima filsuf duduk di meja bundar dengan lima piring spageti (sejenis pasta) dan lima garpu, seperti yang ditunjukkan pada diagram.

Pada waktu tertentu, seorang filsuf pasti sedang makan atau berpikir.

Selain itu, seorang filsuf harus mengambil dua garpu yang berdekatan dengannya (yaitu, garpu kiri dan kanan) sebelum dia bisa makan spageti. Masalah kebuntuan terjadi ketika kelima filsuf mengambil garpu kanan mereka secara bersamaan.

Karena masing-masing filsuf memiliki satu garpu, mereka semua akan menunggu yang lain meletakkan garpu mereka. Akibatnya, tidak ada dari mereka yang bisa makan spageti.

Demikian pula, dalam sistem konkuren, kebuntuan terjadi ketika utas atau proses (filsuf) yang berbeda mencoba untuk memperoleh sumber daya sistem bersama (garpu) pada saat yang sama. Akibatnya, tidak ada proses yang mendapat kesempatan untuk dieksekusi karena menunggu resource lain yang dipegang oleh proses lain.

Kondisi Balapan

Kondisi balapan adalah keadaan program yang tidak diinginkan yang terjadi ketika sistem melakukan dua atau lebih operasi secara bersamaan. Misalnya, pertimbangkan loop for sederhana ini:

i=0; # a global variable
for x in range(100):
    print(i)
    i+=1;

Jika Anda membuat n jumlah utas yang menjalankan kode ini sekaligus, Anda tidak dapat menentukan nilai i (yang dibagi oleh utas) ketika program selesai dieksekusi. Ini karena dalam lingkungan multithreading nyata, utas dapat tumpang tindih, dan nilai i yang diambil dan dimodifikasi oleh utas dapat berubah di antara saat beberapa utas lain mengaksesnya.

Ini adalah dua kelas utama masalah yang dapat terjadi dalam aplikasi python multithreaded atau terdistribusi. Di bagian berikutnya, Anda akan mempelajari cara mengatasi masalah ini dengan menyinkronkan utas.

Menyinkronkan rangkaian pesan

Untuk menangani kondisi balapan, kebuntuan, dan masalah berbasis thread lainnya, modul threading menyediakan Kunci obyek. Idenya adalah ketika sebuah utas menginginkan akses ke sumber daya tertentu, ia memperoleh kunci untuk sumber daya itu. Setelah utas mengunci sumber daya tertentu, tidak ada utas lain yang dapat mengaksesnya hingga kunci dilepaskan. Akibatnya, perubahan pada sumber daya akan bersifat atomik, dan kondisi balapan akan dihindari.

Kunci adalah primitif sinkronisasi tingkat rendah yang diterapkan oleh __thread modul. Pada waktu tertentu, kunci dapat berada di salah satu dari 2 status:terkunci atau tidak terkunci. Ini mendukung dua metode:

  1. memperoleh() Saat status kunci tidak terkunci, memanggil metode acquire() akan mengubah status menjadi terkunci dan kembali. Namun, jika status terkunci, panggilan ke acquire() diblokir hingga metode release() dipanggil oleh beberapa thread lain.
  2. rilis() Metode release() digunakan untuk mengatur status menjadi tidak terkunci, yaitu, untuk melepaskan kunci. Itu dapat dipanggil oleh utas apa pun, tidak harus yang memperoleh kunci.

Berikut ini contoh penggunaan kunci di aplikasi Anda. Jalankan IDLE Anda dan ketik berikut ini:

import threading
lock = threading.Lock()

def first_function():
    for i in range(5):
        lock.acquire()
        print ('lock acquired')
        print ('Executing the first funcion')
        lock.release()

def second_function():
    for i in range(5):
        lock.acquire()
        print ('lock acquired')
        print ('Executing the second funcion')
        lock.release()

if __name__=="__main__":
    thread_one = threading.Thread(target=first_function)
    thread_two = threading.Thread(target=second_function)

    thread_one.start()
    thread_two.start()

    thread_one.join()
    thread_two.join()

Sekarang, tekan F5. Anda akan melihat output seperti ini:

KODE PENJELASAN

  1. Di sini, Anda cukup membuat kunci baru dengan memanggil threading.Lock() fungsi pabrik. Secara internal, Lock() mengembalikan instance kelas Lock beton paling efektif yang dikelola oleh platform.
  2. Dalam pernyataan pertama, Anda memperoleh kunci dengan memanggil metode acquire(). Ketika kunci telah diberikan, Anda mencetak “kunci diperoleh” ke konsol. Setelah semua kode yang Anda inginkan untuk menjalankan utas selesai dieksekusi, Anda melepaskan kunci dengan memanggil metode release().

Teorinya baik-baik saja, tetapi bagaimana Anda tahu bahwa kunci itu benar-benar berfungsi? Jika Anda melihat hasilnya, Anda akan melihat bahwa setiap pernyataan cetak mencetak tepat satu baris pada satu waktu. Ingatlah bahwa, dalam contoh sebelumnya, output dari print di mana serampangan karena beberapa utas mengakses metode print() secara bersamaan. Di sini, fungsi cetak dipanggil hanya setelah kunci diperoleh. Jadi, output ditampilkan satu per satu dan baris demi baris.

Selain kunci, python juga mendukung beberapa mekanisme lain untuk menangani sinkronisasi utas seperti yang tercantum di bawah ini:

  1. Kunci kembali
  2. Semafor
  3. Kondisi
  4. Acara, dan
  5. Hambatan

Kunci Juru Bahasa Global (dan cara mengatasinya)

Sebelum masuk ke detail GIL python, mari kita definisikan beberapa istilah yang akan berguna untuk memahami bagian yang akan datang:

  1. Kode terikat-CPU:ini mengacu pada setiap bagian kode yang akan langsung dieksekusi oleh CPU.
  2. Kode terikat-I/O:ini dapat berupa kode apa pun yang mengakses sistem file melalui 'OS
  3. CPython:ini adalah referensi implementasi Python dan dapat digambarkan sebagai interpreter yang ditulis dalam C dan Python (bahasa pemrograman).

Apa itu GIL di Python?

Kunci Penerjemah Global (GIL) di python adalah kunci proses atau mutex yang digunakan saat berurusan dengan proses. Itu memastikan bahwa satu utas dapat mengakses sumber daya tertentu pada satu waktu dan juga mencegah penggunaan objek dan bytecode sekaligus. Ini menguntungkan program single-threaded dalam peningkatan kinerja. GIL di python sangat sederhana dan mudah diimplementasikan.

Kunci dapat digunakan untuk memastikan bahwa hanya satu utas yang memiliki akses ke sumber daya tertentu pada waktu tertentu.

Salah satu fitur Python adalah menggunakan kunci global pada setiap proses interpreter, yang berarti bahwa setiap proses memperlakukan interpreter python itu sendiri sebagai sumber daya.

Misalnya, Anda telah menulis program python yang menggunakan dua utas untuk melakukan operasi CPU dan 'I/O'. Saat Anda menjalankan program ini, inilah yang terjadi:

  1. Penerjemah python membuat proses baru dan memunculkan utas
  2. Saat thread-1 mulai berjalan, pertama-tama ia akan memperoleh GIL dan menguncinya.
  3. Jika thread-2 ingin dijalankan sekarang, ia harus menunggu GIL dirilis meskipun prosesor lain gratis.
  4. Sekarang, misalkan thread-1 sedang menunggu operasi I/O. Saat ini, GIL akan dirilis, dan thread-2 akan mendapatkannya.
  5. Setelah menyelesaikan operasi I/O, jika thread-1 ingin dieksekusi sekarang, maka harus menunggu lagi GIL dirilis oleh thread-2.

Karena itu, hanya satu utas yang dapat mengakses penerjemah setiap saat, artinya hanya akan ada satu utas yang mengeksekusi kode python pada titik waktu tertentu.

Ini baik-baik saja dalam prosesor single-core karena akan menggunakan time slicing (lihat bagian pertama dari tutorial ini) untuk menangani utas. Namun, dalam kasus prosesor multi-inti, fungsi terikat-CPU yang dijalankan pada banyak utas akan memiliki dampak yang cukup besar pada efisiensi program karena tidak akan benar-benar menggunakan semua inti yang tersedia secara bersamaan.

Mengapa GIL dibutuhkan?

Pengumpul sampah CPython menggunakan teknik manajemen memori yang efisien yang dikenal sebagai penghitungan referensi. Begini cara kerjanya:Setiap objek di python memiliki jumlah referensi, yang meningkat ketika ditugaskan ke nama variabel baru atau ditambahkan ke wadah (seperti tupel, daftar, dll.). Demikian juga, jumlah referensi berkurang ketika referensi keluar dari ruang lingkup atau ketika pernyataan del dipanggil. Ketika jumlah referensi suatu objek mencapai 0, itu adalah sampah yang dikumpulkan, dan memori yang dialokasikan dibebaskan.

Tetapi masalahnya adalah bahwa variabel jumlah referensi rentan terhadap kondisi balapan seperti variabel global lainnya. Untuk mengatasi masalah ini, pengembang python memutuskan untuk menggunakan kunci penerjemah global. Opsi lainnya adalah menambahkan kunci ke setiap objek yang akan mengakibatkan kebuntuan dan peningkatan overhead dari panggilan acquire() dan release().

Oleh karena itu, GIL adalah batasan signifikan untuk program python multithreaded yang menjalankan operasi terikat CPU yang berat (secara efektif menjadikannya single-threaded). Jika Anda ingin menggunakan beberapa inti CPU dalam aplikasi Anda, gunakan multiprosesing modul sebagai gantinya.

Ringkasan

  • Python mendukung 2 modul untuk multithreading:
    1. __utas module:Ini menyediakan implementasi tingkat rendah untuk threading dan sudah usang.
    2. modul threading :Ini memberikan implementasi tingkat tinggi untuk multithreading dan merupakan standar saat ini.
  • Untuk membuat thread menggunakan modul threading, Anda harus melakukan hal berikut:
    1. Buat kelas yang memperluas Utas kelas.
    2. Ganti konstruktornya (__init__).
    3. Ganti run() its metode.
    4. Buat objek kelas ini.
  • Sebuah thread dapat dieksekusi dengan memanggil start() metode.
  • Yang gabung() metode ini dapat digunakan untuk memblokir utas lain hingga utas ini (utas tempat bergabung dipanggil) menyelesaikan eksekusi.
  • Kondisi balapan terjadi saat beberapa utas mengakses atau memodifikasi sumber daya bersama secara bersamaan.
  • Ini dapat dihindari dengan Menyinkronkan utas.
  • Python mendukung 6 cara untuk menyinkronkan utas:
    1. Kunci
    2. Kunci kembali
    3. Semafor
    4. Kondisi
    5. Acara, dan
    6. Hambatan
  • Kunci hanya mengizinkan utas tertentu yang telah memperoleh kunci untuk memasuki bagian kritis.
  • Kunci memiliki 2 metode utama:
    1. memperoleh() :Ini menetapkan status kunci ke terkunci. Jika dipanggil pada objek yang terkunci, objek tersebut akan memblokir hingga sumber dayanya kosong.
    2. rilis() :Ini menyetel status kunci ke tidak terkunci dan kembali. Jika dipanggil pada objek yang tidak terkunci, ia mengembalikan false.
  • Kunci penerjemah global adalah mekanisme yang hanya dapat dijalankan oleh 1 proses penerjemah CPython dalam satu waktu.
  • Ini digunakan untuk memfasilitasi fungsi penghitungan referensi pengumpul sampah CPythons.
  • Untuk membuat aplikasi Python dengan operasi berat terikat CPU, Anda harus menggunakan modul multiprosesor.

Python

  1. free() Fungsi di pustaka C:Bagaimana cara menggunakannya? Belajar dengan Contoh
  2. Python String strip() Fungsi dengan CONTOH
  3. Python String count() dengan CONTOH
  4. Python round() fungsi dengan CONTOH
  5. Python map() berfungsi dengan CONTOH
  6. Python Timeit() dengan Contoh
  7. Penghitung Python dalam Koleksi dengan Contoh
  8. Daftar Python count() dengan CONTOH
  9. Indeks Daftar Python () dengan Contoh
  10. C# - Multithreading