Python'da Eş Zamanlılık - Çoklu İşlem

Bu bölümde, çok işlemcili ve çok iş parçacıklı arasındaki karşılaştırmaya daha fazla odaklanacağız.

Çoklu işlem

Tek bir bilgisayar sistemi içinde iki veya daha fazla CPU biriminin kullanılmasıdır. Bilgisayar sistemimizde bulunan tam sayıda CPU çekirdeğini kullanarak donanımımızdan tam potansiyeli elde etmek için en iyi yaklaşım budur.

Çoklu kullanım

Bir CPU'nun aynı anda birden çok iş parçacığı çalıştırarak işletim sistemi kullanımını yönetebilme yeteneğidir. Çoklu okumanın ana fikri, bir süreci birden çok iş parçacığına bölerek paralellik sağlamaktır.

Aşağıdaki tablo, aralarındaki bazı önemli farkları göstermektedir -

Çoklu işlem Çoklu programlama
Çoklu işlem, birden çok işlemin aynı anda birden çok CPU tarafından işlenmesini ifade eder. Çoklu programlama, birkaç programı aynı anda ana bellekte tutar ve bunları tek bir CPU kullanarak eşzamanlı olarak yürütür.
Birden çok CPU kullanır. Tek CPU kullanır.
Paralel işlemeye izin verir. Bağlam değiştirme gerçekleşir.
İşleri işlemek için daha az zaman harcanır. İşleri işlemek için daha fazla zaman harcanır.
Bilgisayar sistemindeki cihazların çok verimli kullanılmasını kolaylaştırır. Çoklu işlemden daha az verimli.
Genellikle daha pahalıdır. Bu tür sistemler daha ucuzdur.

Global tercüman kilidinin (GIL) etkisini ortadan kaldırma

Eşzamanlı uygulamalarla çalışırken, Python'da adı verilen bir sınırlama vardır. GIL (Global Interpreter Lock). GIL hiçbir zaman birden fazla CPU çekirdeği kullanmamıza izin vermez ve bu nedenle Python'da gerçek iş parçacığı olmadığını söyleyebiliriz. GIL, işleri güvenli hale getiren karşılıklı dışlama kilididir. Başka bir deyişle, GIL'in birden çok iş parçacığının paralel olarak Python kodunu çalıştırmasını engellediğini söyleyebiliriz. Kilit, bir seferde yalnızca bir iş parçacığı tarafından tutulabilir ve eğer bir iş parçacığı yürütmek istiyorsak, önce kilidi almalıdır.

Çoklu işlemin kullanımıyla, GIL'in neden olduğu sınırlamayı etkili bir şekilde atlayabiliriz -

  • Çoklu işlemeyi kullanarak, birden çok işlemin yeteneğini kullanıyoruz ve dolayısıyla GIL'in birden çok örneğini kullanıyoruz.

  • Bu nedenle, herhangi bir zamanda programlarımızda bir iş parçacığının bayt kodunu çalıştırma kısıtlaması yoktur.

Python'da İşlemleri Başlatma

Çoklu işlem modülü içinde Python'da bir işlemi başlatmak için aşağıdaki üç yöntem kullanılabilir -

  • Fork
  • Spawn
  • Forkserver

Fork ile bir süreç oluşturmak

Çatal komutu, UNIX'te bulunan standart bir komuttur. Alt süreçler adı verilen yeni süreçler oluşturmak için kullanılır. Bu alt süreç, üst süreç adı verilen süreçle eşzamanlı olarak çalışır. Bu alt süreçler aynı zamanda üst süreçleriyle aynıdır ve ebeveynin kullanabileceği tüm kaynakları devralır. Fork ile bir işlem oluştururken aşağıdaki sistem çağrıları kullanılır -

  • fork()- Genellikle çekirdekte uygulanan bir sistem çağrısıdır. İşlemin bir kopyasını oluşturmak için kullanılır. P>

  • getpid() - Bu sistem çağrısı, çağıran işlemin işlem kimliğini (PID) döndürür.

Misal

Aşağıdaki Python komut dosyası örneği, yeni bir çocuk sürecin nasıl oluşturulacağını anlamanıza ve alt ve üst süreçlerin PID'lerini almanıza yardımcı olacaktır -

import os

def child():
   n = os.fork()
   
   if n > 0:
      print("PID of Parent process is : ", os.getpid())

   else:
      print("PID of Child process is : ", os.getpid())
child()

Çıktı

PID of Parent process is : 25989
PID of Child process is : 25990

Spawn ile bir süreç oluşturma

Spawn, yeni bir şeye başlamak demektir. Dolayısıyla, bir sürecin ortaya çıkması, bir ana süreç tarafından yeni bir sürecin oluşturulması anlamına gelir. Üst süreç, eşzamansız olarak yürütülmesine devam eder veya alt süreç yürütmeyi bitirene kadar bekler. Bir işlemi oluşturmak için şu adımları izleyin -

  • Çoklu işlem modülü içe aktarılıyor.

  • Nesne sürecini oluşturmak.

  • Süreç etkinliğini arayarak başlatmak start() yöntem.

  • İşlemin işini bitirmesini bekleyin ve arayarak çıkın join() yöntem.

Misal

Aşağıdaki Python betiği örneği, üç işlemin üretilmesine yardımcı olur

import multiprocessing

def spawn_process(i):
   print ('This is process: %s' %i)
   return

if __name__ == '__main__':
   Process_jobs = []
   for i in range(3):
   p = multiprocessing.Process(target = spawn_process, args = (i,))
      Process_jobs.append(p)
   p.start()
   p.join()

Çıktı

This is process: 0
This is process: 1
This is process: 2

Forkserver ile bir süreç oluşturma

Forkserver mekanizması yalnızca dosya tanımlayıcılarının Unix Pipes üzerinden geçirilmesini destekleyen seçilmiş UNIX platformlarında mevcuttur. Forkserver mekanizmasının çalışmasını anlamak için aşağıdaki noktaları göz önünde bulundurun -

  • Yeni süreci başlatmak için Forkserver mekanizması kullanılarak bir sunucu başlatılır.

  • Sunucu daha sonra komutu alır ve yeni işlemler oluşturmak için tüm istekleri işler.

  • Yeni bir süreç oluşturmak için python programımız Forkserver'a bir istek gönderecek ve bizim için bir süreç oluşturacaktır.

  • Nihayet bu yeni oluşturulan süreci programlarımızda kullanabiliriz.

Python'da Daemon işlemleri

Python multiprocessingmodül, arka plan programı seçeneği aracılığıyla daemon işlemlerine sahip olmamızı sağlar. Daemon süreçleri veya arka planda çalışan süreçler, arka planda çalışan iş parçacıklarıyla benzer konsepti izler. İşlemi arka planda yürütmek için, arka planda çalışan bayrağını true olarak ayarlamamız gerekir. Daemon süreci, ana işlem yürütüldüğü sürece çalışmaya devam edecek ve yürütülmesini tamamladıktan sonra veya ana programın sonlandırılmasından sonra sona erecektir.

Misal

Burada daemon evrelerinde kullanılanla aynı örneği kullanıyoruz. Tek fark, modülünmultithreading -e multiprocessingve daemonik bayrağı true olarak ayarlamak. Bununla birlikte, aşağıda gösterildiği gibi çıktıda bir değişiklik olacaktır -

import multiprocessing
import time

def nondaemonProcess():
   print("starting my Process")
   time.sleep(8)
   print("ending my Process")
def daemonProcess():
   while True:
   print("Hello")
   time.sleep(2)
if __name__ == '__main__':
   nondaemonProcess = multiprocessing.Process(target = nondaemonProcess)
   daemonProcess = multiprocessing.Process(target = daemonProcess)
   daemonProcess.daemon = True
   nondaemonProcess.daemon = False
   daemonProcess.start()
   nondaemonProcess.start()

Çıktı

starting my Process
ending my Process

Çıktı, arka plan programı evreleri tarafından oluşturulanla karşılaştırıldığında farklıdır, çünkü hiçbir arka plan programı kipindeki işlemin bir çıktısı yoktur. Bu nedenle, arka plan programı işlemi, çalışan işlemlerin kalıcılığını önlemek için ana programlar sona erdikten sonra otomatik olarak sona erer.

Python'da süreçleri sonlandırma

Kullanarak bir işlemi hemen öldürebilir veya sonlandırabiliriz. terminate()yöntem. Bu yöntemi, işlevin yardımıyla oluşturulan çocuk süreci, yürütülmesini tamamlamadan hemen önce sonlandırmak için kullanacağız.

Misal

import multiprocessing
import time
def Child_process():
   print ('Starting function')
   time.sleep(5)
   print ('Finished function')
P = multiprocessing.Process(target = Child_process)
P.start()
print("My Process has terminated, terminating main thread")
print("Terminating Child Process")
P.terminate()
print("Child Process successfully terminated")

Çıktı

My Process has terminated, terminating main thread
Terminating Child Process
Child Process successfully terminated

Çıktı, programın Child_process () işlevi yardımıyla yaratılan çocuk sürecin yürütülmesinden önce sona erdiğini gösterir. Bu, alt sürecin başarıyla sonlandırıldığı anlamına gelir.

Python'da mevcut süreci tanımlama

İşletim sistemindeki her işlem, PID olarak bilinen işlem kimliğine sahiptir. Python'da, aşağıdaki komut yardımıyla mevcut sürecin PID'sini bulabiliriz -

import multiprocessing
print(multiprocessing.current_process().pid)

Misal

Aşağıdaki Python betiği örneği, ana işlemin PID'sini ve çocuk sürecin PID'sini bulmaya yardımcı olur -

import multiprocessing
import time
def Child_process():
   print("PID of Child Process is: {}".format(multiprocessing.current_process().pid))
print("PID of Main process is: {}".format(multiprocessing.current_process().pid))
P = multiprocessing.Process(target=Child_process)
P.start()
P.join()

Çıktı

PID of Main process is: 9401
PID of Child Process is: 9402

Alt sınıfta bir işlem kullanma

Konuları alt sınıflayarak oluşturabiliriz threading.Threadsınıf. Ayrıca, alt sınıflara ayırarak da süreçler oluşturabiliriz.multiprocessing.Processsınıf. Bir süreci alt sınıfta kullanmak için aşağıdaki noktaları dikkate almamız gerekir -

  • Yeni bir alt sınıf tanımlamamız gerekiyor. Process sınıf.

  • Geçersiz kılmalıyız _init_(self [,args] ) sınıf.

  • Geçersiz kılmalıyız run(self [,args] ) neyi uygulama yöntemi Process

  • Süreci çağırarak başlatmalıyızstart() yöntem.

Misal

import multiprocessing
class MyProcess(multiprocessing.Process):
   def run(self):
   print ('called run method in process: %s' %self.name)
   return
if __name__ == '__main__':
   jobs = []
   for i in range(5):
   P = MyProcess()
   jobs.append(P)
   P.start()
   P.join()

Çıktı

called run method in process: MyProcess-1
called run method in process: MyProcess-2
called run method in process: MyProcess-3
called run method in process: MyProcess-4
called run method in process: MyProcess-5

Python Çoklu İşlem Modülü - Havuz Sınıfı

Basit paralelden bahsedersek processingPython uygulamalarımızdaki görevler, ardından çoklu işlem modülü bize Pool sınıfını sağlar. Aşağıdaki yöntemlerPool sınıf, ana programımızdaki alt süreçlerin sayısını artırmak için kullanılabilir

apply () yöntemi

Bu yöntem,.submit()yöntemi .ThreadPoolExecutor.Sonuç hazır olana kadar bloke eder.

apply_async () yöntemi

Görevlerimizin paralel yürütülmesine ihtiyaç duyduğumuzda,apply_async()havuza görev gönderme yöntemi. Tüm alt işlemler yürütülene kadar ana iş parçacığını kilitlemeyecek zaman uyumsuz bir işlemdir.

map () yöntemi

Tıpkı apply()yöntem, sonuç hazır olana kadar da engeller. Yerleşik olana eşdeğerdirmap() Yinelenebilir verileri birkaç parçaya bölen ve işlem havuzuna ayrı görevler olarak gönderen işlev.

map_async () yöntemi

Bu bir varyantıdır map() yöntem olarak apply_async() için apply()yöntem. Bir sonuç nesnesi döndürür. Sonuç hazır olduğunda, ona bir çağrılabilir uygulanır. Çağrılabilir hemen tamamlanmalıdır; aksi takdirde, sonuçları işleyen iş parçacığı engellenecektir.

Misal

Aşağıdaki örnek, paralel yürütme gerçekleştirmek için bir işlem havuzu uygulamanıza yardımcı olacaktır. Basit bir sayının karesi hesaplaması,square() aracılığıyla işlev multiprocessing.Poolyöntem. Sonrapool.map() 5'i göndermek için kullanıldı, çünkü girdi, 0'dan 4'e kadar olan tamsayıların bir listesidir. Sonuç, p_outputs ve basılmıştır.

def square(n):
   result = n*n
   return result
if __name__ == '__main__':
   inputs = list(range(5))
   p = multiprocessing.Pool(processes = 4)
   p_outputs = pool.map(function_square, inputs)
   p.close()
   p.join()
   print ('Pool :', p_outputs)

Çıktı

Pool : [0, 1, 4, 9, 16]