Zephyrnet Logosu

Python'da Çoklu İş Parçacığı ve Çoklu İşleme Giriş – KDnuggets

Tarih:

Python'da Çoklu İş Parçacığına ve Çoklu İşleme Giriş
Yazara göre resim
 

Bu eğitimde Python'un çoklu iş parçacığı ve çoklu programlama görevlerini yürütme yeteneğinden yararlanma tartışılacaktır. Tek bir işlemde veya birden çok işlemde eşzamanlı işlemleri gerçekleştirmek için bir ağ geçidi sunarlar. Paralel ve eş zamanlı yürütme, sistemlerin hızını ve verimliliğini artırır. Çoklu iş parçacığı ve çoklu programlamanın temellerini tartıştıktan sonra, bunların Python kütüphanelerini kullanarak pratik uygulamalarını da tartışacağız. Öncelikle paralel sistemlerin faydalarından kısaca bahsedelim.

  1. Geliştirilmiş Performans: Görevleri eş zamanlı olarak gerçekleştirebilme yeteneği sayesinde yürütme süresini azaltabilir ve sistemin genel performansını iyileştirebiliriz.
  2. Ölçeklenebilirlik: Büyük bir görevi çeşitli küçük alt görevlere bölebilir ve bağımsız yürütmeleri için bunlara ayrı bir çekirdek veya iş parçacığı atayabiliriz. Büyük ölçekli sistemlerde yardımcı olabilir.
  3. Verimli G/Ç İşlemleri: Eşzamanlılığın yardımıyla CPU'nun bir işlemin G/Ç işlemlerini tamamlamasını beklemesi gerekmez. CPU, önceki işlem kendi G/Ç'si ile meşgul olana kadar hemen aşağıdaki işlemi yürütmeye başlayabilir.
  4. Kaynak Optimizasyonu: Kaynakları bölerek tek bir sürecin tüm kaynakları kaplamasını önleyebiliriz. Bu sorunu önleyebilir Açlık daha küçük süreçler için.

 

Python'da Çoklu İş Parçacığına ve Çoklu İşleme Giriş
Paralel Hesaplamanın Faydaları | Yazara ait resim
 

Bunlar eşzamanlı veya paralel yürütmelere ihtiyaç duymanızın bazı yaygın nedenleridir. Şimdi ana konulara, yani Çoklu İş Parçacığı ve Çoklu Programlamaya geri dönün ve bunların temel farklarını tartışın.

Çoklu iş parçacığı, tek bir süreçte paralellik sağlamanın ve eşzamanlı görevleri yürütebilmenin yollarından biridir. Tek bir işlem içinde birden fazla iş parçacığı oluşturulabilir ve bu işlem içinde paralel olarak daha küçük görevler gerçekleştirilebilir. 

Tek bir işlem içinde bulunan iş parçacıkları ortak bir bellek alanını paylaşır, ancak bunların yığın izleri ve kayıtları farklıdır. Bu paylaşılan hafıza nedeniyle hesaplama açısından daha az pahalıdırlar.

 

Python'da Çoklu İş Parçacığına ve Çoklu İşleme Giriş
Tek Dişli ve Çok Dişli Zarf | Resim: GeeksForGeeks
 

Çoklu iş parçacığı öncelikle G/Ç işlemlerinin gerçekleştirilmesinde kullanılır, yani programın bir kısmı G/Ç işlemleriyle meşgulse, geri kalan program yanıt verebilir. Ancak Python'un uygulamasında, Global Interpreter Lock (GIL) nedeniyle çoklu iş parçacığı gerçek paralelliğe ulaşamaz.

Kısaca GIL, aynı anda yalnızca bir iş parçacığının Python bayt koduyla etkileşime girmesine izin veren bir muteks kilididir; yani, çok iş parçacıklı modda bile, bir seferde yalnızca bir iş parçacığı bayt kodunu çalıştırabilir.

CPython'da iş parçacığı güvenliğini korumak için yapılır, ancak bu, çoklu iş parçacığının performans avantajlarını sınırlar. Bu sorunu çözmek için Python'un daha sonra tartışacağımız ayrı bir çoklu işlem kütüphanesi vardır.

Daemon Konuları nedir?

Arka planda sürekli çalışan iş parçacıklarına şeytan iş parçacıkları denir. Ana görevleri ana iş parçacığını veya arka plan programı olmayan iş parçacıklarını desteklemektir. Daemon iş parçacığı ana iş parçacığının yürütülmesini engellemez ve hatta yürütmeyi tamamlamış olsa bile çalışmaya devam eder.

Python'da daemon iş parçacıkları çoğunlukla çöp toplayıcı olarak kullanılır. Tüm gereksiz nesneleri yok edecek ve ana iş parçacığının düzgün şekilde kullanılabilmesi ve çalıştırılabilmesi için varsayılan olarak belleği boşaltacaktır.

Çoklu işlem, birden fazla işlemin paralel yürütülmesini gerçekleştirmek için kullanılır. Ayrı süreçleri aynı anda yürüttüğümüz ve kendi bellek alanlarına sahip olduğumuz için gerçek paralelliğe ulaşmamıza yardımcı olur. CPU'nun ayrı çekirdeklerini kullanır ve aynı zamanda birden fazla işlem arasında veri alışverişi yapmak için işlemler arası iletişimin gerçekleştirilmesinde de yardımcı olur.

Çoklu işlem, çoklu iş parçacığına kıyasla hesaplama açısından daha pahalıdır, çünkü paylaşılan bir bellek alanı kullanmamaktayız. Yine de bağımsız uygulama yapmamıza olanak tanır ve Global Interpreter Lock'un sınırlamalarının üstesinden gelir.

 

Python'da Çoklu İş Parçacığına ve Çoklu İşleme Giriş
Çoklu İşlem Ortamı | Resim: GeeksForGeeks
 

Yukarıdaki şekil, bir ana sürecin iki ayrı süreç oluşturduğu ve bunlara ayrı işler atadığı çoklu işlem ortamını göstermektedir.

Python kullanarak çoklu iş parçacığının temel bir örneğini uygulamanın zamanı geldi. Python'un yerleşik bir modülü vardır threading çoklu iş parçacığı uygulaması için kullanılır.

  1. Kitaplıkları İçe Aktarma:
import threading
import os

 

  1. Kareleri Hesaplama Fonksiyonu:

Bu, sayıların karesini bulmak için kullanılan basit bir fonksiyondur. Giriş olarak bir sayı listesi verilir ve listedeki her sayının karesi, kullanılan iş parçacığının adı ve bu iş parçacığıyla ilişkili işlem kimliğiyle birlikte çıktı olarak verilir.

def calculate_squares(numbers):
    for num in numbers:
        square = num * num
        print(
            f"Square of the number {num} is {square} | Thread Name {threading.current_thread().name} | PID of the process {os.getpid()}"
        )

 

  1. Ana Fonksiyon:

Elimizde bir sayı listesi var ve bu listeyi eşit olarak bölüp fisrt_half ve olarak adlandıracağız. second_half sırasıyla. Şimdi iki ayrı thread atayacağız t1 ve t2 bu listelere.

Thread işlev, bir işlevi o işleve ilişkin argümanların listesini alan yeni bir iş parçacığı oluşturur. Ayrıca bir konuya ayrı bir ad da atayabilirsiniz.

.start() işlev bu konuları yürütmeye başlayacak ve .join() işlevi, verilen iş parçacığı tamamen yürütülene kadar ana iş parçacığının yürütülmesini engelleyecektir.

if __name__ == "__main__":
    numbers = [1, 2, 3, 4, 5, 6, 7, 8]
    half = len(numbers) // 2
    first_half = numbers[:half]
    second_half = numbers[half:]

    t1 = threading.Thread(target=calculate_squares, name="t1", args=(first_half,))
    t2 = threading.Thread(target=calculate_squares, name="t2", args=(second_half,))

    t1.start()
    t2.start()

    t1.join()
    t2.join()

 

Çıktı:

Square of the number 1 is 1 | Thread Name t1 | PID of the process 345
Square of the number 2 is 4 | Thread Name t1 | PID of the process 345
Square of the number 5 is 25 | Thread Name t2 | PID of the process 345
Square of the number 3 is 9 | Thread Name t1 | PID of the process 345
Square of the number 6 is 36 | Thread Name t2 | PID of the process 345
Square of the number 4 is 16 | Thread Name t1 | PID of the process 345
Square of the number 7 is 49 | Thread Name t2 | PID of the process 345
Square of the number 8 is 64 | Thread Name t2 | PID of the process 345

 

Not: Yukarıda oluşturulan tüm iş parçacıkları daemon olmayan iş parçacıklarıdır. Bir daemon iş parçacığı oluşturmak için şunu yazmanız gerekir: t1.setDaemon(True) ipliği yapmak için t1 bir daemon iş parçacığı.

 

Şimdi yukarıdaki kodun ürettiği çıktıyı anlayacağız. İşlem kimliğinin (yani PID) her iki iş parçacığı için aynı kalacağını gözlemleyebiliriz, bu da bu iki iş parçacığının aynı işlemin parçası olduğu anlamına gelir.

Ayrıca çıktının sıralı olarak üretilmediğini de gözlemleyebilirsiniz. İlk satırda thread1 tarafından üretilen çıktıyı, ardından 3. satırda thread2 tarafından üretilen çıktıyı, ardından dördüncü satırda tekrar thread1 tarafından üretilen çıktıyı göreceksiniz. Bu açıkça bu iş parçacıklarının eş zamanlı olarak birlikte çalıştığını gösterir.

Eşzamanlılık, aynı anda yalnızca bir iş parçacığı yürütüldüğünden, bu iki iş parçacığının paralel olarak yürütüldüğü anlamına gelmez. Yürütme süresini azaltmaz. Sıralı yürütme ile aynı süreyi alır. CPU bir iş parçacığını yürütmeye başlar ancak onu yarıda bırakır ve başka bir iş parçacığına geçer ve bir süre sonra ana iş parçacığına geri döner ve en son kaldığı yerden yürütmeye başlar.

Umarım çoklu iş parçacıklı kullanımın uygulanması ve sınırlamaları konusunda temel bir anlayışa sahipsinizdir. Artık çoklu işlem uygulamasını ve bu sınırlamaların üstesinden nasıl gelebileceğimizi öğrenmenin zamanı geldi. 

Aynı örneği takip edeceğiz ancak iki ayrı konu oluşturmak yerine iki bağımsız süreç oluşturup gözlemleri tartışacağız.

  1. Kitaplıkları İçe Aktarma:
from multiprocessing import Process
import os

 

Biz kullanacağız multiprocessing Bağımsız süreçler oluşturmak için modül. 

  1. Kareleri Hesaplama Fonksiyonu:

Bu işlev aynı kalacak. İş parçacığı bilgilerinin yazdırma ifadesini henüz kaldırdık.

def calculate_squares(numbers):
    for num in numbers:
        square = num * num
        print(
            f"Square of the number {num} is {square} | PID of the process {os.getpid()}"
        )

 

  1. Ana Fonksiyon:

Ana fonksiyonda birkaç değişiklik var. Bir iş parçacığı yerine ayrı bir süreç oluşturduk.

if __name__ == "__main__":
    numbers = [1, 2, 3, 4, 5, 6, 7, 8]
    half = len(numbers) // 2
    first_half = numbers[:half]
    second_half = numbers[half:]

    p1 = Process(target=calculate_squares, args=(first_half,))
    p2 = Process(target=calculate_squares, args=(second_half,))

    p1.start()
    p2.start()

    p1.join()
    p2.join()

 

Çıktı:

Square of the number 1 is 1 | PID of the process 1125
Square of the number 2 is 4 | PID of the process 1125
Square of the number 3 is 9 | PID of the process 1125
Square of the number 4 is 16 | PID of the process 1125
Square of the number 5 is 25 | PID of the process 1126
Square of the number 6 is 36 | PID of the process 1126
Square of the number 7 is 49 | PID of the process 1126
Square of the number 8 is 64 | PID of the process 1126

 

Her listenin ayrı bir proses tarafından yürütüldüğünü gözlemledik. Her ikisinin de farklı işlem kimlikleri vardır. Süreçlerimizin paralel yürütülüp yürütülmediğini kontrol etmek için aşağıda tartışacağımız ayrı bir ortam oluşturmamız gerekiyor.

Çoklu İşlemle ve Çoklu İşlem olmadan Çalışma Süresini Hesaplama

Gerçek bir paralellik elde edip etmediğimizi kontrol etmek için, algoritmanın çoklu işlemli ve çoklu işlemsiz çalışma süresini hesaplayacağız.

Bunun için 10^6'dan fazla tam sayı içeren kapsamlı bir tam sayı listesine ihtiyacımız olacak. kullanarak bir liste oluşturabiliriz. random kütüphane. kullanacağız time Çalışma zamanını hesaplamak için Python modülü. Aşağıda buna ilişkin uygulama yer almaktadır. Kod kendi kendini açıklamaktadır, ancak kod yorumlarına her zaman bakabilirsiniz.

from multiprocessing import Process
import os
import time
import random

def calculate_squares(numbers):
    for num in numbers:
        square = num * num

if __name__ == "__main__":
    numbers = [
        random.randrange(1, 50, 1) for i in range(10000000)
    ]  # Creating a random list of integers having size 10^7.
    half = len(numbers) // 2
    first_half = numbers[:half]
    second_half = numbers[half:]

    # ----------------- Creating Single Process Environment ------------------------#

    start_time = time.time()  # Start time without multiprocessing

    p1 = Process(
        target=calculate_squares, args=(numbers,)
    )  # Single process P1 is executing all list
    p1.start()
    p1.join()

    end_time = time.time()  # End time without multiprocessing
    print(f"Execution Time Without Multiprocessing: {(end_time-start_time)*10**3}ms")

    # ----------------- Creating Multi Process Environment ------------------------#

    start_time = time.time()  # Start time with multiprocessing

    p2 = Process(target=calculate_squares, args=(first_half,))
    p3 = Process(target=calculate_squares, args=(second_half,))

    p2.start()
    p3.start()

    p2.join()
    p3.join()

    end_time = time.time()  # End time with multiprocessing
    print(f"Execution Time With Multiprocessing: {(end_time-start_time)*10**3}ms")

 

Çıktı:

Execution Time Without Multiprocessing: 619.8039054870605ms
Execution Time With Multiprocessing: 321.70287895202637ms

 

Çoklu işlem sırasında sürenin, çoklu işlem yapılmamasına kıyasla neredeyse yarı yarıya olduğunu gözlemleyebilirsiniz. Bu, bu iki işlemin aynı anda aynı anda yürütüldüğünü ve gerçek paralellik davranışı sergilediğini gösterir.

Bu makaleyi de okuyabilirsiniz Sıralı vs Eşzamanlı vs Paralellik Bu Sıralı, Eşzamanlı ve Paralel süreçler arasındaki temel farkı anlamanıza yardımcı olacak Medium'dan.
 
 

Aryan Garg bir B.Tech'tir. Elektrik Mühendisliği öğrencisi, şu anda lisans eğitiminin son yılında. İlgi alanı Web Geliştirme ve Makine Öğrenimi alanında yatmaktadır. Bu ilginin peşinden gitti ve bu yönlerde daha fazla çalışmak için can atıyorum.

spot_img

En Son İstihbarat

spot_img