Çoklu İşlem Kuyruğu, Havuz ve Kilitleme kullanmanın son derece basit bir örneği


92

Http://docs.python.org/dev/library/multiprocessing.html adresindeki belgeleri okumaya çalıştım, ancak hala çoklu işlem Kuyruğu, Havuz ve Kilitleme ile uğraşıyorum. Ve şimdilik aşağıdaki örneği oluşturabildim.

Kuyruk ve Havuz ile ilgili olarak, kavramı doğru şekilde anladığımdan emin değilim, bu yüzden yanılıyorsam düzeltin. Başarmaya çalıştığım şey aynı anda 2 isteği işlemek (bu örnekte veri listesinde 8 tane var), peki ne kullanmalıyım? Havuz, iki farklı kuyruğu (en fazla 2) işleyebilen 2 işlem oluşturmak için mi yoksa her seferinde 2 girişi işlemek için yalnızca Kuyruk mu kullanmalıyım? Kilit, çıktıları doğru şekilde yazdırmak olacaktır.

import multiprocessing
import time

data = (['a', '2'], ['b', '4'], ['c', '6'], ['d', '8'],
        ['e', '1'], ['f', '3'], ['g', '5'], ['h', '7']
)


def mp_handler(var1):
    for indata in var1:
        p = multiprocessing.Process(target=mp_worker, args=(indata[0], indata[1]))
        p.start()


def mp_worker(inputs, the_time):
    print " Processs %s\tWaiting %s seconds" % (inputs, the_time)
    time.sleep(int(the_time))
    print " Process %s\tDONE" % inputs

if __name__ == '__main__':
    mp_handler(data)

Yanıtlar:


130

Sorununuz için en iyi çözüm, bir Pool. S kullanmak Queueve ayrı bir "kuyruk besleme" işlevine sahip olmak muhtemelen gereğinden fazla olur.

İşte programın biraz yeniden düzenlenmiş versiyonu ile bu zamanı sadece 2 süreçlerde bir de coralled Pool. Orijinal kodda en az değişiklikle gitmenin en kolay yolu olduğuna inanıyorum:

import multiprocessing
import time

data = (
    ['a', '2'], ['b', '4'], ['c', '6'], ['d', '8'],
    ['e', '1'], ['f', '3'], ['g', '5'], ['h', '7']
)

def mp_worker((inputs, the_time)):
    print " Processs %s\tWaiting %s seconds" % (inputs, the_time)
    time.sleep(int(the_time))
    print " Process %s\tDONE" % inputs

def mp_handler():
    p = multiprocessing.Pool(2)
    p.map(mp_worker, data)

if __name__ == '__main__':
    mp_handler()

mp_worker()İşlevin artık tek bir bağımsız değişkeni (önceki iki bağımsız değişkenin bir demeti) kabul ettiğini unutmayın, çünkü map()işlev girdi verilerinizi alt listelere böler, her alt liste, çalışan işlevinize tek bir bağımsız değişken olarak verilir.

Çıktı:

Processs a  Waiting 2 seconds
Processs b  Waiting 4 seconds
Process a   DONE
Processs c  Waiting 6 seconds
Process b   DONE
Processs d  Waiting 8 seconds
Process c   DONE
Processs e  Waiting 1 seconds
Process e   DONE
Processs f  Waiting 3 seconds
Process d   DONE
Processs g  Waiting 5 seconds
Process f   DONE
Processs h  Waiting 7 seconds
Process g   DONE
Process h   DONE

Aşağıdaki @ Thales yorumuna göre düzenleyin:

İşlemlerinizin ardışık çiftler halinde çalışması için "her havuz sınırı için bir kilit" istiyorsanız, ala:

Bekleyen B bekliyor | A bitti, B bitti | C bekliyor, D bekliyor | C bitti, D bitti | ...

ardından işleyici işlevini, her veri çifti için havuzları (2 işlemden oluşan) başlatmak için değiştirin:

def mp_handler():
    subdata = zip(data[0::2], data[1::2])
    for task1, task2 in subdata:
        p = multiprocessing.Pool(2)
        p.map(mp_worker, (task1, task2))

Şimdi çıktınız:

 Processs a Waiting 2 seconds
 Processs b Waiting 4 seconds
 Process a  DONE
 Process b  DONE
 Processs c Waiting 6 seconds
 Processs d Waiting 8 seconds
 Process c  DONE
 Process d  DONE
 Processs e Waiting 1 seconds
 Processs f Waiting 3 seconds
 Process e  DONE
 Process f  DONE
 Processs g Waiting 5 seconds
 Processs h Waiting 7 seconds
 Process g  DONE
 Process h  DONE

Nasıl yapılacağına dair basit ve doğrudan örnek için teşekkürler, Ama her havuz limiti için kilidi nasıl uygulayabilirim? Demek istediğim, kodu çalıştırırsanız, "A bekliyor B bekliyor | A bitti, b bitti | C bekliyor, D bekliyor | C bitti, D bitti"
thclpr

2
Başka bir deyişle, hem A hem de B bitene kadar C'nin başlamasını istemiyor musunuz?
Velimir Mlaker

Kesinlikle, bunu çoklu işlem kullanarak yapabilirim.Process ama havuzu kullanarak nasıl yapacağımı
çözemiyorum

Çok teşekkür ederim, amaçlandığı gibi çalışın, ancak mp_handler fonksiyonunda var1 yerine değişken veriye başvuruyorsunuz :)
thclpr

Tamam teşekkürler, bunun yerine var1globalden bahsederek tamamen kaldırdım data.
Velimir Mlaker

9

Bu, soruyla% 100 ilgili olmayabilir, ancak aramamda bir kuyrukla çoklu işlemeyi kullanma örneği için bu, google'da ilk olarak görünür.

Bu, öğeleri başlatıp bir kuyruğa yerleştirebileceğiniz ve sıra bitene kadar bekleyebileceğiniz temel bir örnek sınıftır. Tek ihtiyacım olan buydu.

from multiprocessing import JoinableQueue
from multiprocessing.context import Process


class Renderer:
    queue = None

    def __init__(self, nb_workers=2):
        self.queue = JoinableQueue()
        self.processes = [Process(target=self.upload) for i in range(nb_workers)]
        for p in self.processes:
            p.start()

    def render(self, item):
        self.queue.put(item)

    def upload(self):
        while True:
            item = self.queue.get()
            if item is None:
                break

            # process your item here

            self.queue.task_done()

    def terminate(self):
        """ wait until queue is empty and terminate processes """
        self.queue.join()
        for p in self.processes:
            p.terminate()

r = Renderer()
r.render(item1)
r.render(item2)
r.terminate()

2
Nelerdir item1ve item2? Bunlar iki farklı süreçte yürütülecek bir tür görev veya işlev mi?
Zelphir Kaltstahl

2
evet bunlar paralel bir şekilde işlenen görevler veya girdi parametreleridir.
linqu

8

İşte bu konu için kişisel yolum:

Ana bilgi burada, (istekleri kabul edin!): Https://gist.github.com/thorsummoner/b5b1dfcff7e7fdd334ec

import multiprocessing
import sys

THREADS = 3

# Used to prevent multiple threads from mixing thier output
GLOBALLOCK = multiprocessing.Lock()


def func_worker(args):
    """This function will be called by each thread.
    This function can not be a class method.
    """
    # Expand list of args into named args.
    str1, str2 = args
    del args

    # Work
    # ...



    # Serial-only Portion
    GLOBALLOCK.acquire()
    print(str1)
    print(str2)
    GLOBALLOCK.release()


def main(argp=None):
    """Multiprocessing Spawn Example
    """
    # Create the number of threads you want
    pool = multiprocessing.Pool(THREADS)

    # Define two jobs, each with two args.
    func_args = [
        ('Hello', 'World',), 
        ('Goodbye', 'World',), 
    ]


    try:
        # Spawn up to 9999999 jobs, I think this is the maximum possible.
        # I do not know what happens if you exceed this.
        pool.map_async(func_worker, func_args).get(9999999)
    except KeyboardInterrupt:
        # Allow ^C to interrupt from any thread.
        sys.stdout.write('\033[0m')
        sys.stdout.write('User Interupt\n')
    pool.close()

if __name__ == '__main__':
    main()

1
.Map_async () 'in herhangi bir şekilde .map ()' den daha iyi olduğundan tam olarak emin değilim.
ThorSummoner

3
Argümanı get()bir zaman aşımıdır, başlatılan işlerin sayısıyla hiçbir ilgisi yoktur.
mata

@mata yani, bu bir yoklama döngüsünde kullanılacak mı? .get(timeout=1)? ve sadece .get()tamamlanan listeyi al demekte bir sakınca var mı?
ThorSummoner

Evet, .get()tüm sonuçlar mevcut olana kadar süresiz olarak bekler ve sonuçların listesini verir. Hava durumu sonuçlarının mevcut olup olmadığını kontrol etmek için bir yoklama döngüsü kullanabilir veya map_async()çağrıda, kullanılabilir olduğunda her sonuç için çağrılacak olan bir geri arama işlevini iletebilirsiniz .
mata

2

Komodo Edit (win10) gibi editörleri kullanan herkes için şunları ekleyin sys.stdout.flush():

def mp_worker((inputs, the_time)):
    print " Process %s\tWaiting %s seconds" % (inputs, the_time)
    time.sleep(int(the_time))
    print " Process %s\tDONE" % inputs
    sys.stdout.flush()

veya ilk satır olarak:

    if __name__ == '__main__':
       sys.stdout.flush()

Bu, betiğin çalıştırılması sırasında neler olup bittiğini görmenize yardımcı olur; siyah komut satırı kutusuna bakmak yerine.


1

İşte kodumdan bir örnek (iş parçacıklı havuz için, ancak yalnızca sınıf adını değiştirin ve işlem havuzunuz olacak):

def execute_run(rp): 
   ... do something 

pool = ThreadPoolExecutor(6)
for mat in TESTED_MATERIAL:
    for en in TESTED_ENERGIES:
        for ecut in TESTED_E_CUT:
            rp = RunParams(
                simulations, DEST_DIR,
                PARTICLE, mat, 960, 0.125, ecut, en
            )
            pool.submit(execute_run, rp)
pool.join()

Temel olarak:

  • pool = ThreadPoolExecutor(6) 6 iş parçacığı için bir havuz oluşturur
  • Sonra havuza görev ekleyen bir sürü for'unuz var
  • pool.submit(execute_run, rp) havuza bir görev ekler, ilk argüman bir evre / süreçte çağrılan bir fonksiyondur, geri kalan argümanlar çağrılan fonksiyona iletilir.
  • pool.join tüm görevler tamamlanana kadar bekler.

2
Kullandığınızı concurrent.futures, ancak OP'nin multiprocessingPython 2.7 hakkında soru sorduğunu unutmayın .
Tim Peters
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.