python multithreading tüm iş parçacıkları bitene kadar bekleyin


119

Bu, benzer bir bağlamda sorulmuş olabilir, ancak yaklaşık 20 dakika aradıktan sonra bir cevap bulamadım, bu yüzden soracağım.

Bir Python betiği (diyelim ki: scriptA.py) ve bir betik (scriptB.py diyelim) yazdım

ScriptB'de scriptA'yı farklı argümanlarla birden çok kez çağırmak istiyorum, her seferinde çalıştırılması yaklaşık bir saat sürüyor (çok büyük bir komut dosyası, pek çok şey yapıyor ... bunun için endişelenmeyin) ve çalıştırabilmek istiyorum. scriptA ile aynı anda tüm farklı argümanlar var, ancak devam etmeden önce TÜMÜNÜN bitmesini beklemem gerekiyor; benim kodum:

import subprocess

#setup
do_setup()

#run scriptA
subprocess.call(scriptA + argumentsA)
subprocess.call(scriptA + argumentsB)
subprocess.call(scriptA + argumentsC)

#finish
do_finish()

Hepsini subprocess.call()aynı anda çalıştırmak ve sonra hepsi bitene kadar beklemek istiyorum, bunu nasıl yapmalıyım?

Buradaki örnek gibi iş parçacığı kullanmayı denedim :

from threading import Thread
import subprocess

def call_script(args)
    subprocess.call(args)

#run scriptA   
t1 = Thread(target=call_script, args=(scriptA + argumentsA))
t2 = Thread(target=call_script, args=(scriptA + argumentsB))
t3 = Thread(target=call_script, args=(scriptA + argumentsC))
t1.start()
t2.start()
t3.start()

Ama bunun doğru olduğunu düşünmüyorum.

Bana gitmeden önce tümünün koşmayı bitirdiğini nasıl bilebilirim do_finish()?

Yanıtlar:


150

Betiğin sonunda nesne birleştirme yöntemini kullanmanız gerekir Thread.

t1 = Thread(target=call_script, args=(scriptA + argumentsA))
t2 = Thread(target=call_script, args=(scriptA + argumentsB))
t3 = Thread(target=call_script, args=(scriptA + argumentsC))

t1.start()
t2.start()
t3.start()

t1.join()
t2.join()
t3.join()

Böylece ana iş parçacığı kadar bekleyecek t1, t2ve t3yürütme bitirmek.


5
hmmm - bir şeyi anlamakta güçlük çekiyorsanız, bunu önce t1'i çalıştırmayacaksınız, bitene kadar bekleyiniz, sonra t2'ye gittiniz .. vb. bunların hepsi aynı anda nasıl olur? aynı anda onları nasıl çalıştıracağını anlamıyorum?
Inbar Gül

25
İş joinparçacığı yürütmeyi bitirene kadar engelleme çağrısı . Yine de tüm iş parçacıklarını beklemeniz gerekecek. Eğer t1bitirir öncelikle bekleyen başlayacak t2(zaten bitmiş olabilir ve hemen beklemek devam edecek olan t3). Eğer t1bunu hem döndüğünüzde, yürütülecek uzun sürdü t1ve t2engellemeden hemen dönecektir.
Maksim Skurydzin

1
Sorumu anlamıyorsun - yukarıdaki kodu koduma kopyalarsam işe yarayacak mı? yoksa bir şey mi kaçırıyorum?
Inbar Rose

2
tamam anlıyorum. şimdi anlıyorum, biraz kafam karışmıştı ama sanırım anlıyorum, joinşu anki süreci iş parçacığına bağlar ve bitene kadar bekler ve eğer t2 t1'den önce biterse o zaman t1 bittiğinde t2'nin bitip bitmediğini kontrol eder. öyle ve sonra t3..etc..etc .. 'yi kontrol edin ve sadece hepsi tamamlandığında devam edecektir. müthiş.
Inbar Gül

3
diyelim ki t1 en uzun sürüyor ama t2'nin bir istisnası var. O zaman ne olacak? bu istisnayı yakalayabilir misin veya t2'nin tamam olup olmadığını kontrol edebilir misin?
Ciprian Tomoiagă

174

Konuları bir listeye koyun ve ardından Join yöntemini kullanın

 threads = []

 t = Thread(...)
 threads.append(t)

 ...repeat as often as necessary...

 # Start all threads
 for x in threads:
     x.start()

 # Wait for all of them to finish
 for x in threads:
     x.join()

1
Evet, işe yarardı ama anlaşılması daha zor. Her zaman kompakt kod ve "okunabilirlik" arasında bir denge bulmaya çalışmalısınız. Unutmayın: Kod bir kez yazılır, ancak birçok kez okunur. Bu yüzden anlaşılması kolay olması daha önemli.
Aaron Digulla

2
"Fabrika kalıbı" tek bir cümleyle açıklayabileceğim bir şey değil. Bunun için Google ve stackoverflow.com'da arama yapın. Pek çok örnek ve açıklama var. Özetle: Sizin için karmaşık bir şey oluşturan bir kod yazarsınız. Gerçek bir fabrika gibi: Sipariş verirsiniz ve bitmiş bir ürünü geri alırsınız.
Aaron Digulla

18
Yan etkileri için liste anlamayı kullanma ve ortaya çıkan listeyle yararlı hiçbir şey yapmama fikrini sevmiyorum. Basit bir for döngüsü iki sıra
yaysa

1
@Aaron DIgull Bunu anlıyorum. Demek istediğim, for x in threads: x.join()liste anlama kullanmak yerine sadece bir yapacağım
Ioan Alexandru Cucu

1
@IoanAlexandruCucu: Hala daha okunabilir ve verimli bir çözüm olup olmadığını merak ediyorum: stackoverflow.com/questions/21428602/…
Aaron Digulla

29

Python3'te, Python 3.2'den beri aynı sonuca ulaşmak için yeni bir yaklaşım var, kişisel olarak geleneksel iş parçacığı oluşturma / başlatma / birleştirme paketini tercih ediyorum concurrent.futures: https://docs.python.org/3/library/concurrent.futures .html

Bir ThreadPoolExecutorkod kullanmak şöyle olacaktır:

from concurrent.futures.thread import ThreadPoolExecutor
import time

def call_script(ordinal, arg):
    print('Thread', ordinal, 'argument:', arg)
    time.sleep(2)
    print('Thread', ordinal, 'Finished')

args = ['argumentsA', 'argumentsB', 'argumentsC']

with ThreadPoolExecutor(max_workers=2) as executor:
    ordinal = 1
    for arg in args:
        executor.submit(call_script, ordinal, arg)
        ordinal += 1
print('All tasks has been finished')

Önceki kodun çıktısı şuna benzer:

Thread 1 argument: argumentsA
Thread 2 argument: argumentsB
Thread 1 Finished
Thread 2 Finished
Thread 3 argument: argumentsC
Thread 3 Finished
All tasks has been finished

Avantajlarından biri, maksimum eşzamanlı çalışanı ayarlayarak verimi kontrol edebilmenizdir.


ama iş parçacığı havuzundaki tüm iş parçacıklarının bittiğini nasıl anlarsınız?
Başbakan By Design

1
Örnekte görebileceğiniz gibi, withtüm görev tamamlandığında ifadeden sonraki kod yürütülür.
Roberto

bu çalışmıyor. İplerde gerçekten uzun bir şey yapmayı deneyin. Baskı deyiminiz iş parçacığı bitmeden yürütülecek
Pranalee

@Pranalee, Bu kod çalışıyor, çıktı satırlarını eklemek için kodu güncelledim. Tüm iş parçacıkları bitmeden önce "Tüm görev ..." öğesini göremezsiniz with. Bu durumda ifade tasarım gereği böyle çalışır. Her neyse, SO'da her zaman yeni bir soru açabilir ve kodunuzu gönderebilirsiniz, böylece durumunuzda neler olduğunu öğrenmenize yardımcı olabiliriz.
Roberto

@PrimeByDesign concurrent.futures.waitişlevi kullanabilirsiniz , burada gerçek bir örnek görebilirsiniz Resmi belgeler: docs.python.org/3/library/…
Alexander Fortin

28

Bir girdi listesine dayalı olarak liste anlamayı kullanmayı tercih ederim:

inputs = [scriptA + argumentsA, scriptA + argumentsB, ...]
threads = [Thread(target=call_script, args=(i)) for i in inputs]
[t.start() for t in threads]
[t.join() for t in threads]

İşaretli cevap iyi açıklıyor ama bu daha kısa ve çirkin tekrarlar gerektirmiyor. Sadece güzel bir cevap. :)
tleb

Yalnızca yan etkiler için listenin anlaşılması genellikle amortismana tabi tutulur *. Ancak bu kullanım durumunda iyi bir fikir gibi görünüyor. * stackoverflow.com/questions/5753597/…
Vinayak Kaniyarakkal

3
@VinayakKaniyarakkal for t in threads:t.start()daha iyi değil mi?
SmartManoj

5

Paralel tutku içinde yürütmek istediğiniz 'n' sayıda işlev veya console_scripts ekleyebileceğiniz ve yürütmeyi başlatabileceğiniz ve tüm işlerin tamamlanmasını bekleyebileceğiniz aşağıdaki gibi bir şeyi sınıflandırabilirsiniz.

from multiprocessing import Process

class ProcessParallel(object):
    """
    To Process the  functions parallely

    """    
    def __init__(self, *jobs):
        """
        """
        self.jobs = jobs
        self.processes = []

    def fork_processes(self):
        """
        Creates the process objects for given function deligates
        """
        for job in self.jobs:
            proc  = Process(target=job)
            self.processes.append(proc)

    def start_all(self):
        """
        Starts the functions process all together.
        """
        for proc in self.processes:
            proc.start()

    def join_all(self):
        """
        Waits untill all the functions executed.
        """
        for proc in self.processes:
            proc.join()


def two_sum(a=2, b=2):
    return a + b

def multiply(a=2, b=2):
    return a * b


#How to run:
if __name__ == '__main__':
    #note: two_sum, multiply can be replace with any python console scripts which
    #you wanted to run parallel..
    procs =  ProcessParallel(two_sum, multiply)
    #Add all the process in list
    procs.fork_processes()
    #starts  process execution 
    procs.start_all()
    #wait until all the process got executed
    procs.join_all()


3

Gönderen threading modül belgelerinde

Bir "ana iş parçacığı" nesnesi vardır; bu, Python programındaki ilk kontrol iş parçacığına karşılık gelir. Bu bir artalan süreci dizisi değildir.

"Yapay iplik nesnelerinin" yaratılma olasılığı vardır. Bunlar, doğrudan C kodundan olduğu gibi, iş parçacığı modülünün dışında başlatılan kontrol konuları olan "yabancı iş parçacıkları" na karşılık gelen iplik nesneleridir. Sahte iş parçacığı nesneleri sınırlı işlevselliğe sahiptir; her zaman canlı ve şeytani olarak kabul edilirler ve eğitilemezler join(). Yabancı konuların sonlandırılmasını tespit etmek imkansız olduğu için asla silinmezler.

Bu nedenle, oluşturduğunuz konuların bir listesini tutmakla ilgilenmediğinizde bu iki durumu yakalamak için:

import threading as thrd


def alter_data(data, index):
    data[index] *= 2


data = [0, 2, 6, 20]

for i, value in enumerate(data):
    thrd.Thread(target=alter_data, args=[data, i]).start()

for thread in thrd.enumerate():
    if thread.daemon:
        continue
    try:
        thread.join()
    except RuntimeError as err:
        if 'cannot join current thread' in err.args[0]:
            # catchs main thread
            continue
        else:
            raise

bunun üzerine:

>>> print(data)
[0, 4, 12, 40]

2

Belki bir şey

for t in threading.enumerate():
    if t.daemon:
        t.join()

Bu kodu denedim ama çalıştığından emin değilim çünkü kodumun son talimatı bundan sonra for döngüsünden sonra basıldı ve yine de işlem sonlandırılmadı.
Omkar

1

For döngüsü kullanılarak oluşturulan tüm iş parçacıkları için beklemem gereken aynı problemle karşılaştım.Aşağıdaki kod parçasını denedim.Mükemmel bir çözüm olmayabilir ama basit bir çözüm olacağını düşündüm test etmek için:

for t in threading.enumerate():
    try:
        t.join()
    except RuntimeError as err:
        if 'cannot join current thread' in err:
            continue
        else:
            raise
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.