Bir sonuç kuyruğunu birkaç işlem arasında paylaşma


96

multiprocessingModül belgeleri, bir kuyruğun başlatılan bir işleme nasıl geçirileceğini gösterir multiprocessing.Process. Ancak bir kuyruğu, başlatılan zaman uyumsuz çalışan işlemlerle nasıl paylaşabilirim apply_async? Dinamik birleşmeye veya başka bir şeye ihtiyacım yok, sadece çalışanların sonuçlarını (tekrar tekrar) üsse geri bildirmeleri için bir yol.

import multiprocessing
def worker(name, que):
    que.put("%d is done" % name)

if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=3)
    q = multiprocessing.Queue()
    workers = pool.apply_async(worker, (33, q))

Bu başarısız: RuntimeError: Queue objects should only be shared between processes through inheritance. Bunun ne anlama geldiğini anlıyorum ve dekapaj / çözme (ve tüm özel Windows kısıtlamalarını) gerektirmektense miras alma tavsiyesini anlıyorum. Ama nasıl yok ben eserler bir şekilde kuyruğu geçmesi? Bir örnek bulamıyorum ve çeşitli şekillerde başarısız olan birkaç alternatif denedim. Yardım lütfen?

Yanıtlar:


138

Kuyruğunuzu yönetmek ve aynı zamanda farklı çalışanlar için erişilebilir hale getirmek için çoklu işlem yöneticisi kullanmayı deneyin .

import multiprocessing
def worker(name, que):
    que.put("%d is done" % name)

if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=3)
    m = multiprocessing.Manager()
    q = m.Queue()
    workers = pool.apply_async(worker, (33, q))

Başardı, teşekkürler! Orijinal kodumdaki eşzamansız çağrı ile ilgisiz bir sorun vardı, bu yüzden düzeltmeyi sizin cevabınıza da kopyaladım.
alexis

17
Bunun için neden queue.Queue()uygun olmadığına dair herhangi bir açıklama var mı?
mrgloom

1
@mrgloom: queue.Queuebellek içi kilitler kullanılarak iş parçacığı oluşturmak için oluşturuldu. Bir Çoklu İşlem ortamında, alt işlemler queue.Queue()belleği (çoğunlukla) paylaşmadığından , her alt işlem bir örneğin kendi bellek alanında kendi kopyasını alır .
LeoRochael

1
@alexis Birden fazla çalışan veri ekledikten sonra Manager (). Queue () öğesinden öğeler nasıl alınır?
MSS


14

multiprocessing.Poolzaten paylaşılan bir sonuç kuyruğuna sahipse, ek olarak bir Manager.Queue. ayrı bir sunucu işleminde bulunan ve proxy'ler aracılığıyla açığa çıkan, kaputun altındaki Manager.Queuebir queue.Queue(çok iş parçacıklı sıradır). Bu, Havuzun dahili kuyruğuna kıyasla ek yük getirir. Pool'un yerel sonuç işlemesine güvenmenin aksine, sonuçların Manager.Queueda sipariş edilmesi garanti edilmez.

Çalışan işlemler vardır değil ile başladı .apply_async()başlatmanızı bu zaten olur, Pool. Ne olduğunu Aradığınızda başlayan pool.apply_async()yeni bir "iş" tir. multiprocessing.pool.workerHavuzun işçi süreçleri , kaputun altında işlevini çalıştırır . Bu işlev, Havuzun dahili üzerinden aktarılan yeni "görevleri" işlemeyi Pool._inqueueve sonuçları üst öğeye üzerinden geri göndermeyi halleder Pool._outqueue. Belirttiğiniz funcalan içinde yürütülecektir multiprocessing.pool.worker. funcsadece bir returnşeye ihtiyaç duyar ve sonuç otomatik olarak ebeveyne geri gönderilir.

.apply_async() hemen (zaman uyumsuz olarak) bir AsyncResultnesne (takma adı ) döndürür ApplyResult. .get()Gerçek sonucu almak için o nesneyi aramanız (engelliyor) gerekir . Diğer bir seçenek de, sonuç hazır olur olmaz ateşlenen bir geri arama işlevi kaydetmektir .

from multiprocessing import Pool

def busy_foo(i):
    """Dummy function simulating cpu-bound work."""
    for _ in range(int(10e6)):  # do stuff
        pass
    return i

if __name__ == '__main__':

    with Pool(4) as pool:
        print(pool._outqueue)  # DEMO
        results = [pool.apply_async(busy_foo, (i,)) for i in range(10)]
        # `.apply_async()` immediately returns AsyncResult (ApplyResult) object
        print(results[0])  # DEMO
        results = [res.get() for res in results]
        print(f'result: {results}')       

Örnek Çıktı:

<multiprocessing.queues.SimpleQueue object at 0x7fa124fd67f0>
<multiprocessing.pool.ApplyResult object at 0x7fa12586da20>
result: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Not: timeout-parametresinin belirtilmesi, .get()worker içindeki görevin fiili işlenmesini durdurmaz, yalnızca bir multiprocessing.TimeoutError.


İlginç, ilk fırsatta deneyeceğim. 2012'de kesinlikle bu şekilde
alexis

@alexis Python 2.7 relevantly burada (2010) sadece bağlam yöneticisi ve eksik error_callbackiçin -parametre apply_asynco zamandan beri pek değişmedi, böylece.
Darkonaut

Geri çağırma işlevinin, özellikle burada açıklandığı gibi eşzamansız sonuçları toplamak için normal bir listenin kullanılmasına izin vermek için kısmi bir işlevle birleştirildiğinde en yararlı olduğunu buldum; gist.github.com/Glench/5789879
user5359531
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.