Çoklu iş parçacığı Python Örnek ile: GIL'yi öğrenin Python

⚡ Akıllı Özet

Çoklu iş parçacığı Python runs several threads inside one process so they share memory and work concurrently. The threading module creates and manages these threads, while the Global Interpreter Lock limits true parallelism, making the technique best for input/output-bound tasks.

  • 🔘 Thread vs process: A thread is a unit of execution inside a process, and threads share the process data while running individually.
  • ☑️ Two modules: The obsolete _thread module offers low-level calls, while the higher-level threading module is the modern standard for building threads.
  • Create threads: Extend the Thread class, override __init__ and run(), then call start() to launch and join() to wait for completion.
  • 🧪 Avoid race conditions: A Lock lets only one thread enter a critical section at a time, preventing deadlocks and corrupted shared data.
  • Understand the GIL: The Global Interpreter Lock allows one thread to run Python bytecode at a time, so CPU-bound work needs multiprocessing.
  • 🤖 Yapay zeka iş yükleri: Multithreading speeds input/output-bound AI steps like data loading, while NumPy and PyTorch release the GIL for parallel computation.

Çoklu iş parçacığı Python

MKS Python programlama dili çoklu işlem veya çoklu iş parçacığı kullanmanıza olanak tanır. Bu eğitimde, çok iş parçacıklı uygulamaların nasıl yazılacağını öğreneceksiniz. Python.

Konu Nedir?

A thread is a unit of execution in concurrent programming. Multithreading is a technique that allows a CPU to execute many tasks of one process at the same time. These threads can execute individually while sharing their process resources.

Süreç Nedir?

A process is basically the program in execution. When you start an application on your computer (like a browser or text editor), the operating system creates a proses.

Çoklu iş parçacığı nedir? Python?

Çoklu iş parçacığı Python programming is a well-known technique in which multiple threads in a process share their data space with the main thread, which makes information sharing and communication within threads easy and efficient. Threads are lighter than processes. Multiple threads may execute individually while sharing their process resources. The purpose of multithreading is to run multiple tasks and functions at the same time.

Çoklu İşleme Nedir?

Çoklu İşlem birden fazla ilgisiz işlemi aynı anda çalıştırmanıza olanak tanır. Bu işlemler kaynaklarını paylaşmaz ve IPC aracılığıyla iletişim kurar.

Python Çoklu iş parçacığı vs Çoklu işlem

To understand processes and threads, consider this scenario: An .exe file on your computer is a program. When you open it, the OS loads it into memory, and the CPU executes it. The instance of the program that is now running is called the process.

Every process has two fundamental components:

  • MKS Code
  • Veri

Artık bir süreç, adı verilen bir veya daha fazla alt parça içerebilir. İş Parçacığı. This depends on the OS architecture. You can think of a thread as a section of the process that can be executed separately by the operating system.

In other words, it is a stream of instructions that can be run independently by the OS. Threads within a single process share the data of that process and are designed to work together to facilitate parallelism.

Neden Çoklu İş Parçacığı kullanmalı?

Çoklu iş parçacığı, bir uygulamayı birden fazla alt göreve bölmenize ve bu görevleri aynı anda çalıştırmanıza olanak tanır. Çoklu iş parçacığını doğru şekilde kullanırsanız, uygulama hızınız, performansınız ve işlemeniz iyileştirilebilir.

Python Çok iş parçacığı

Python supports constructs for both multiprocessing and multithreading. In this tutorial, you will primarily focus on implementing okuyuculu olan uygulamalar Python. There are two main modules that can be used to handle threads in Python:

  1. MKS iplik modül ve
  2. MKS threading modül

Ancak Python, there is also something called a global interpreter lock (GIL). It does not allow for much performance gain and may even azaltmak bazı çok iş parçacıklı uygulamaların performansı. Bu eğitimin gelecek bölümlerinde bununla ilgili her şeyi öğreneceksiniz.

Thread ve Threading modülleri

Bu eğitimde öğreneceğiniz iki modül şunlardır: iplik modülü ve iş parçacığı modülü.

Ancak iş parçacığı modülü uzun süredir kullanımdan kaldırılmıştır. Şununla başlıyor: Python 3, eski olarak belirlenmiş ve yalnızca şu şekilde erişilebilir: _thread geriye dönük uyumluluk için.

Daha üst seviyeyi kullanmalısınız threading module for applications that you intend to deploy. The thread module has only been covered here for educational purposes.

Konu Modülü

Bu modülü kullanarak yeni bir iş parçacığı oluşturmanın sözdizimi aşağıdaki gibidir:

thread.start_new_thread(function_name, arguments)

Tamam, artık kodlamaya başlamanın temel teorisini öğrendiniz. Öyleyse aç IDLE veya bir not defterine yazın ve şunları yazın:

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))

Dosyayı kaydedin ve programı çalıştırmak için F5 tuşuna basın. Her şey doğru yapıldıysa görmeniz gereken çıktı şudur:

Konu Modülü

You will learn more about race conditions and how to handle them in the upcoming sections.

Konu Modülü

KOD AÇIKLAMA

  1. These statements import the time and thread module, which are used to handle the execution and delaying of the Python İş Parçacığı.
  2. Burada, adı verilen bir işlevi tanımladınız. thread_test, tarafından çağrılacak start_new_thread method. The function runs a while loop for four iterations and prints the name of the thread that called it. Once the iteration is complete, it prints a message saying that the thread has finished execution.
  3. Bu programınızın ana bölümüdür. Burada aramanız yeterli start_new_thread ile yöntem iş parçacığı_testi function as an argument. This will create a new thread for the function you pass as an argument and start executing it. Note that you can replace this (thread_test) with any other function that you want to run as a thread.

Diş Açma Modülü

Bu modül, iş parçacığının üst düzey uygulamasıdır Python ve çok iş parçacıklı uygulamaları yönetmek için fiili standart. İplik modülüyle karşılaştırıldığında geniş bir özellik yelpazesi sunar.

Diş Açma modülünün yapısı

Diş Açma modülünün yapısı

Bu modülde tanımlanan bazı yararlı işlevlerin listesi aşağıda verilmiştir:

Fonksiyon adı Açıklama
aktifSayı() Sayısını döndürür Konu objects that are still alive.
currentThread() Thread sınıfının geçerli nesnesini döndürür.
numaralandırmak() Tüm etkin Thread nesnelerini listeler.
isDaemon() İş parçacığı bir daemon ise true değerini döndürür.
yaşıyor() İş parçacığı hala hayattaysa true değerini döndürür.
Konu Sınıfı yöntemleri
Başlat() Bir iş parçacığının etkinliğini başlatır. Her iş parçacığı için yalnızca bir kez çağrılmalıdır çünkü birden çok kez çağrılırsa çalışma zamanı hatası verir.
Çalıştırmak() Bu yöntem bir iş parçacığının etkinliğini belirtir ve İş Parçacığı sınıfını genişleten bir sınıf tarafından geçersiz kılınabilir.
katılmak() join() yönteminin çağrıldığı iş parçacığı sonlandırılana kadar diğer kodun yürütülmesini engeller.

Arka Plan Hikayesi: Konu Sınıfı

Before you start coding multithreaded programs using the threading module, it is crucial to understand the Thread class. The thread class is the primary class that defines the template and the operations of a thread in Python.

Çok iş parçacıklı bir oluşturmanın en yaygın yolu Python application is to declare a class that extends the Thread class and overrides its run() method.

Thread sınıfı özetle ayrı bir kod dizisinde çalışan bir kod dizisini ifade eder. iplik kontrol

Yani, çok iş parçacıklı bir uygulama yazarken şunları yapacaksınız:

  1. define a class that extends the Thread class
  2. Geçersiz kıl __init__ inşaatçı
  3. Geçersiz kıl Çalıştırmak() yöntem

Bir iş parçacığı nesnesi oluşturulduktan sonra, Başlat() method can be used to begin the execution of this activity, and the katılmak() yöntemi, mevcut etkinlik bitene kadar diğer tüm kodları engellemek için kullanılabilir.

Now, let us try using the threading module to implement your previous example. Again, fire up your IDLE ve aşağıdakileri yazın:

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()

Yukarıdaki kodu çalıştırdığınızda çıktı bu olacaktır:

Arka Plan Hikayesi: Konu Sınıfı

KOD AÇIKLAMA

Arka Plan Hikayesi: Konu Sınıfı

  1. This part is the same as our previous example. Here, you import the time and thread module, which are used to handle the execution and delays of the Python İş Parçacığı.
  2. Bu bitte, threadtester adında, miras alan veya genişleten bir sınıf yaratıyorsunuz. Konu iş parçacığı modülünün sınıfı. Bu, iş parçacığı oluşturmanın en yaygın yollarından biridir. Python. Ancak, yalnızca yapıcıyı ve Çalıştırmak() uygulamanızda yöntem. Yukarıdaki kod örneğinde görebileceğiniz gibi, __init__ yöntem (yapıcı) geçersiz kılındı. Benzer şekilde, şunu da geçersiz kıldınız: Çalıştırmak() yöntem. Bir iş parçacığının içinde çalıştırmak istediğiniz kodu içerir. Bu örnekte thread_test() fonksiyonunu çağırdınız.
  3. This is the thread_test() method, which takes the value of i as an argument, decreases it by 1 at each iteration, and loops through the rest of the code until i becomes 0. In each iteration, it prints the name of the currently executing thread and sleeps for wait seconds (which is also taken as an argument).
  4. thread1 = threadtester(1, “First Thread”, 1) Burada bir thread oluşturuyoruz ve __init__'de bildirdiğimiz üç parametreyi geçiyoruz. İlk parametre iş parçacığının kimliği, ikinci parametre iş parçacığının adı ve üçüncü parametre ise while döngüsünün kaç kez çalışması gerektiğini belirleyen sayaçtır.
  5. thread2.start() The start method is used to start the execution of a thread. Internally, the start() function calls the run() method of your class.
  6. thread3.join() join() yöntemi diğer kodun yürütülmesini engeller ve çağrıldığı iş parçacığının tamamlanmasını bekler.

As you already know, the threads that are in the same process have access to the memory and data of that process. As a result, if more than one thread tries to change or access the data simultaneously, errors may creep in.

In the next section, you will see the different kinds of complications that can show up when threads access data and the critical section without checking for existing access transactions.

Kilitlenmeler ve Yarış koşulları

Before learning about deadlocks and race conditions, it will be helpful to understand a few basic definitions related to concurrent programming:

  • Kritik Bölüm: It is a fragment of code that accesses or modifies shared variables and must be performed as an atomic transaction.
  • Bağlam Değiştirme: It is the process that a CPU follows to store the state of a thread before changing from one task to another so that it can be resumed from the same point later.

kilitlenmeler

kilitlenmeler geliştiricilerin eşzamanlı/çok iş parçacıklı uygulamalar yazarken karşılaştıkları en korkulan sorundur. Python. The best way to understand deadlocks is by using the classic computer science example problem known as the Gastronomi Philosophers Sorunu.

Yemek felsefecileri için problem ifadesi şu şekildedir:

Five philosophers are seated at a round table with five plates of spaghetti (a type of pasta) and five forks, as shown in the diagram.

Gastronomi Philosophers Sorunu

Gastronomi Philosophers Sorunu

Bir filozofun herhangi bir anda ya yemek yediği ya da düşündüğü anlaşılıyor.

Dahası, bir filozof spagettiyi yiyebilmek için önce yanındaki iki çatalı (yani sol ve sağ çatalı) almak zorundadır. Kilitlenme sorunu, beş filozofun da sağ çatallarını aynı anda almasıyla ortaya çıkar.

Filozofların her birinin bir çatalı olduğundan, hepsi diğerlerinin çatallarını bırakmasını bekleyeceklerdir. Sonuç olarak, hiçbiri spagetti yiyemeyecektir.

Benzer şekilde, eşzamanlı bir sistemde, farklı iş parçacıkları veya süreçler (filozoflar) aynı anda paylaşılan sistem kaynaklarını (çatallar) edinmeye çalıştığında bir çıkmaz meydana gelir. Sonuç olarak, süreçlerin hiçbiri başka bir süreç tarafından tutulan başka bir kaynağı bekledikleri için yürütülme şansı elde edemez.

Yarış koşulları

A race condition is an unwanted state of a program that occurs when a system performs two or more operations simultaneously. For example, consider this simple for loop:

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

eğer yaratırsan n number of threads that run this code at once, you cannot determine the value of i (which is shared by the threads) when the program finishes execution. This is because in a real multithreading environment, the threads can overlap, and the value of i that was retrieved and modified by a thread can change in between when some other thread accesses it.

Bunlar, çok iş parçacıklı veya dağıtılmış bir sistemde meydana gelebilecek iki ana sorun sınıfıdır. Python application. In the next section, you will learn how to overcome this problem by synchronizing threads.

Synckonuları hronize etmek

Yarış koşulları, çıkmazlar ve diğer iş parçacığı tabanlı sorunlarla başa çıkmak için iş parçacığı modülü şunları sağlar: kilitlemek nesne. Fikir, bir iş parçacığının belirli bir kaynağa erişmek istemesi durumunda, o kaynak için bir kilit edinmesidir. Bir iş parçacığı belirli bir kaynağı kilitlediğinde, kilit serbest bırakılana kadar başka hiçbir iş parçacığı ona erişemez. Sonuç olarak, kaynaktaki değişiklikler atomik olacak ve yarış koşulları önlenecektir.

Bir kilit, tarafından uygulanan düşük seviyeli bir senkronizasyon ilkelidir. _thread module. At any given time, a lock can be in one of two states: kilitli or kilitsiz. İki yöntemi destekler:

  1. acquire(): When the lock state is unlocked, calling the acquire() method will change the state to locked and return. However, if the state is locked, the call to acquire() is blocked until the release() method is called by some other thread.
  2. release(): Release() yöntemi, durumu kilitsiz olarak ayarlamak, yani bir kilidi serbest bırakmak için kullanılır. Kilidi alan herhangi bir iş parçacığı tarafından çağrılabilir.

Here is an example of using locks in your apps. Fire up your IDLE ve aşağıdakileri yazın:

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()

Şimdi F5'e basın. Bunun gibi bir çıktı görmelisiniz:

SyncKonuları kronikleştirme

KOD AÇIKLAMA

SyncKonuları kronikleştirme

  1. Burada sadece çağrı yaparak yeni bir kilit oluşturuyorsunuz. iş parçacığı.Lock() fabrika işlevi. Dahili olarak Lock(), platform tarafından sağlanan en etkili somut Lock sınıfının bir örneğini döndürür.
  2. İlk ifadede, gain() yöntemini çağırarak kilidi elde edersiniz. Kilit verildiğinde yazdırırsınız “kilit alındı” konsola. İş parçacığının çalışmasını istediğiniz tüm kodun yürütmesi bittiğinde, Release() yöntemini çağırarak kilidi serbest bırakırsınız.

The theory is fine, but how do you know that the lock really worked? If you look at the output, you will see that each of the print statements is printing exactly one line at a time. Recall that, in an earlier example, the outputs from print were haphazard because multiple threads were accessing the print() method at the same time. Here, the print function is called only after the lock is acquired. So, the outputs are displayed one at a time and line by line.

Kilitlerin yanı sıra Python also supports some other mechanisms to handle thread synchronization, as listed below:

  1. RKilitler
  2. Semaphores
  3. Koşullar
  4. Etkinlikler ve
  5. Bariyerler

Küresel Tercüman Kilidi (ve bununla nasıl başa çıkılacağı)

Ayrıntılara girmeden önce Python’s GIL, let us define a few terms that will be useful in understanding the upcoming section:

  1. CPU-bound code: this refers to any piece of code that will be directly executed by the CPU.
  2. I/O-bound code: this can be any code that accesses the file system through the OS.
  3. CPython: bu referanstır uygulama of Python ve C ile yazılmış yorumlayıcı olarak tanımlanabilir ve Python (programlama dili).

GIL nedir? Python?

Küresel Tercüman Kilidi (GIL) in Python is a process lock or a mutex used while dealing with the processes. It makes sure that one thread can access a particular resource at a time, and it also prevents the use of objects and bytecodes at once. This benefits the single-threaded programs with a performance increase. GIL in Python uygulaması oldukça basit ve kolaydır.

Belirli bir zamanda yalnızca bir iş parçacığının belirli bir kaynağa erişebildiğinden emin olmak için kilit kullanılabilir.

özelliklerinden biri Python her tercüman sürecinde global bir kilit kullanmasıdır; bu da her sürecin, Python tercümanın kendisi bir kaynaktır.

Örneğin, bir yazı yazdığınızı varsayalım. Python program that uses two threads to perform both CPU and ‘I/O’ operations. When you execute this program, this is what happens:

  1. MKS Python interpreter creates a new process and spawns the threads.
  2. İş parçacığı-1 çalışmaya başladığında ilk önce GIL'yi alacak ve kilitleyecektir.
  3. Eğer iş parçacığı-2 şimdi yürütülmek istiyorsa, başka bir işlemci boş olsa bile GIL'in serbest bırakılmasını beklemek zorunda kalacak.
  4. Şimdi iş parçacığı-1'in bir G/Ç işlemini beklediğini varsayalım. Şu anda GIL'i serbest bırakacak ve iş parçacığı-2 onu alacak.
  5. G/Ç işlemlerini tamamladıktan sonra, eğer iş parçacığı-1 şimdi yürütmek istiyorsa, yine GIL'in iş parçacığı-2 tarafından serbest bırakılmasını beklemek zorunda kalacak.

Bu nedenle, herhangi bir zamanda yorumlayıcıya yalnızca bir iş parçacığı erişebilir, bu da yalnızca bir iş parçacığının yürütüleceği anlamına gelir. Python code at a given point in time.

This is alright in a single-core processor because it would be using time slicing (see the first section of this tutorial) to handle the threads. However, in the case of multi-core processors, a CPU-bound function executing on multiple threads will have a considerable impact on the program’s efficiency since it will not actually be using all the available cores at the same time.

GIL'e neden ihtiyaç duyuldu?

CPython garbage collector uses an efficient memory management technique known as reference counting. Here is how it works: Every object in Python has a reference count, which is increased when it is assigned to a new variable name or added to a container (like tuples, lists, etc.). Likewise, the reference count is decreased when the reference goes out of scope or when the del statement is called. When the reference count of an object reaches 0, it is garbage collected, and the allotted memory is freed.

Ancak sorun, referans sayısı değişkeninin diğer küresel değişkenler gibi yarış koşullarına yatkın olmasıdır. Bu sorunu çözmek için geliştiriciler Python decided to use the global interpreter lock. The other option was to add a lock to each object, which would have resulted in deadlocks and increased overhead from acquire() and release() calls.

Bu nedenle GIL, çok iş parçacıklı uygulamalar için önemli bir kısıtlamadır. Python programs running heavy CPU-bound operations (effectively making them single-threaded). If you want to make use of multiple CPU cores in your application, use the çoklu işlem modül yerine.

SSS

Use threading for I/O-bound work such as network calls, file access, or database queries, where threads wait on external resources. Use multiprocessing for CPU-bound work such as heavy calculations, because it runs on multiple cores and bypasses the GIL.

Not by default. PEP 703 introduced an optional free-threaded build in Python 3.13 that disables the GIL, and Python 3.14 continues it. Standard builds still ship the GIL, so most programs keep running exactly as before.

A daemon thread runs in the background and does not block the program from exiting. When only daemon threads remain, Python shuts them down and quits. Set one with thread.daemon = True before calling start().

The Thread class does not return the run() result directly. Store the value on the thread object or a shared queue, or use concurrent.futures.ThreadPoolExecutor, whose submit() method returns a Future you read with result().

ThreadPoolExecutor is a high-level helper that manages a pool of worker threads for you. You submit tasks with submit() or map(), and it handles thread creation, reuse, and cleanup, returning Future objects that hold each result.

You can create hundreds or thousands of threads, but the GIL lets only one execute Python bytecode at any instant. They still overlap during I/O waits, so many concurrent threads help I/O-bound programs far more than CPU-bound ones.

Yes, mainly for I/O-bound steps such as loading data, calling APIs, or reading files. Heavy training is CPU-bound, so libraries like NumPy and PyTorch release the GIL in C code or rely on multiprocessing for true parallelism.

Yes. GitHub Copilot autocompletes common patterns such as Thread subclasses, Lock usage, and ThreadPoolExecutor setups from a comment or function name. Always review the generated code for race conditions and correct lock handling before relying on it.

Bu yazıyı şu şekilde özetleyin: