Çok işlemli: Sınıfta tanımlanan bir işlevde Pool.map nasıl kullanılır?


179

Ne zaman bir şey çalıştırmak:

from multiprocessing import Pool

p = Pool(5)
def f(x):
     return x*x

p.map(f, [1,2,3])

iyi çalışıyor. Ancak, bunu bir sınıfın işlevi olarak koymak:

class calculate(object):
    def run(self):
        def f(x):
            return x*x

        p = Pool()
        return p.map(f, [1,2,3])

cl = calculate()
print cl.run()

Bana şu hatayı verir:

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/sw/lib/python2.6/threading.py", line 532, in __bootstrap_inner
    self.run()
  File "/sw/lib/python2.6/threading.py", line 484, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/sw/lib/python2.6/multiprocessing/pool.py", line 225, in _handle_tasks
    put(task)
PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed

Alex Martelli'den de aynı tür bir sorunla uğraşan bir gönderi gördüm, ama yeterince açık değildi.


1
"bu bir sınıfın fonksiyonu olarak"? Asıl hatayı alan kodu gönderebilir misiniz? Gerçek kod olmadan sadece yanlış yaptığınızı tahmin edebiliriz.
S.Lott

Genel bir açıklama olarak, Python'un standart turşu modülünden daha güçlü dekapaj modülleri vardır ( bu cevapta bahsedilen picloud modülü gibi ).
klaus se

1
Kapanışlarla benzer bir sorunum vardı IPython.Parallel, ama nesneleri düğümlere iterek sorunu çözebilirsiniz. Çok işlemeyle bu sorunun üstesinden gelmek oldukça can sıkıcı görünüyor.
Alex S

Burada calculatebu kopyalar üzerinde olduğu, bir yapılandırıcı ile, bir işlev nesnesini oluşturmak: 1) çözülebilir Bu gibi görünüyor, bu yüzden picklable olan calculate2 örneği ve daha sonra), bu işlev nesnesinin bir örneğini geçen Pool'in mapyöntemi. Hayır?
rd11

1
Math Python'un "son değişikliklerinin" hiçbirinin yararlı olacağına inanmıyorum. multiprocessingModülün bazı sınırlamaları, platformlar arası bir uygulama olma hedefinden ve fork(2)Windows'ta benzer bir sistem çağrısının olmamasından kaynaklanmaktadır . Win32 desteği ile ilgilenmiyorsanız, daha basit bir işlem tabanlı geçici çözüm olabilir. Yerine süreçlerin konuları kullanmaya hazır eğer Veya, yerini alabilir from multiprocessing import Poolile from multiprocessing.pool import ThreadPool as Pool.
Aya

Yanıtlar:


69

Ben de pool.map fonksiyonları ne tür kabul edebilir kısıtlamaları rahatsız oldu. Bunu atlatmak için aşağıdakileri yazdım. Parmağın tekrarlı kullanımı için bile çalışıyor gibi görünüyor.

from multiprocessing import Process, Pipe
from itertools import izip

def spawn(f):
    def fun(pipe, x):
        pipe.send(f(x))
        pipe.close()
    return fun

def parmap(f, X):
    pipe = [Pipe() for x in X]
    proc = [Process(target=spawn(f), args=(c, x)) for x, (p, c) in izip(X, pipe)]
    [p.start() for p in proc]
    [p.join() for p in proc]
    return [p.recv() for (p, c) in pipe]

if __name__ == '__main__':
    print parmap(lambda x: x**x, range(1, 5))

1
Bu benim için çok işe yaradı, teşekkürler. Bir zayıflık buldum: Parçayı varsayılan bir çözümden geçen ve PicklingError'ı tekrar alan bazı işlevlerde kullanmayı denedim. Buna bir çözüm bulamadım, sadece defaultdict'ı kullanmamak için kodumu yeniden çalıştım.
sans

2
Bu Python 2.7.2'de çalışmıyor (varsayılan, 12 Haz 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] win32'de
ubershmekel

3
Bu Python 2.7.3 Ağu 1.2012, 05:14:39 üzerinde çalışır. Bu dev yinelenebilirler üzerinde çalışmaz -> bir OSError neden olur: [Errno 24] Açtığı boru sayısı nedeniyle çok fazla açık dosya.
Eiyrioü von Kauyf

Bu çözüm, her iş öğesi için bir süreç oluşturur. Aşağıdaki "klaus se" nin çözümü daha verimlidir.
ypnos

85

Şimdiye kadar gönderilen kodları kullanamadım çünkü "multiprocessing.Pool" kullanan kodlar lambda ifadeleri ile çalışmaz ve kodlar "multiprocessing.Pool" kullanmayan iş öğeleri kadar çok süreç oluşturur.

Ben önceden belirlenmiş miktarda işçi doğurmak st kodunu adapte ve sadece boş bir işçi varsa giriş listesi üzerinden tekrarlar. Ben de işçi st ctrl-c beklendiği gibi çalışır "daemon" modunu etkinleştirdi.

import multiprocessing


def fun(f, q_in, q_out):
    while True:
        i, x = q_in.get()
        if i is None:
            break
        q_out.put((i, f(x)))


def parmap(f, X, nprocs=multiprocessing.cpu_count()):
    q_in = multiprocessing.Queue(1)
    q_out = multiprocessing.Queue()

    proc = [multiprocessing.Process(target=fun, args=(f, q_in, q_out))
            for _ in range(nprocs)]
    for p in proc:
        p.daemon = True
        p.start()

    sent = [q_in.put((i, x)) for i, x in enumerate(X)]
    [q_in.put((None, None)) for _ in range(nprocs)]
    res = [q_out.get() for _ in range(len(sent))]

    [p.join() for p in proc]

    return [x for i, x in sorted(res)]


if __name__ == '__main__':
    print(parmap(lambda i: i * 2, [1, 2, 3, 4, 6, 7, 8]))

2
Bu parmapişlevle düzgün çalışmak için bir ilerleme çubuğunu nasıl elde edersiniz ?
shockburner

2
Bir soru - Bu çözümü kullandım, ancak doğduğum python işlemlerinin bellekte aktif kaldığını fark ettim. Parmağınız çıktığında bunları nasıl öldüreceğinizle ilgili hızlı bir düşünce var mı?
CompEcon

1
@ klaus-se Sadece yorumlarda teşekkür etmekten vazgeçtiğimizi biliyorum, ama cevabınız benim için çok değerli, dayanamadım. Keşke sana sadece bir
ünden

2
(None, None)Son öğe olarak geçen @greole fun, her işlem için öğe dizisinin sonuna geldiğini gösterir .
aganders3

4
@deshtop: Kendinize yeteri kadar itibarınız varsa bir ödül ile yapabilirsiniz :-)
Mark

57

Standart kitaplığın dışına çıkmadığınız sürece çoklu işleme ve dekapaj kırılır ve sınırlandırılır.

Eğer bir çatal kullanırsanız multiprocessingdenilen pathos.multiprocesssing, doğrudan en çoklu işlem sınıfları ve sınıf yöntemlerini kullanabilirsiniz mapfonksiyonlar. Bunun nedeni dill, pickleveya yerine kullanılır cPickleve dillpython'daki hemen hemen her şeyi serileştirebilir.

pathos.multiprocessingAyrıca uyumsuz bir harita işlevi sağlar ... ve can map(örn birden argümanlarla fonksiyonlar map(math.pow, [1,2,3], [4,5,6]))

Tartışmalara bakın: Çok işlemli dereotu birlikte ne yapabilir?

ve: http://matthewrocklin.com/blog/work/2013/12/05/Parallelism-ve-Serialization

Başlangıçta, değiştirmeden ve yorumlayıcıdan yazdığınız kodu bile işler. Neden daha kırılgan ve tek bir vakaya özgü başka bir şey yapıyorsunuz?

>>> from pathos.multiprocessing import ProcessingPool as Pool
>>> class calculate(object):
...  def run(self):
...   def f(x):
...    return x*x
...   p = Pool()
...   return p.map(f, [1,2,3])
... 
>>> cl = calculate()
>>> print cl.run()
[1, 4, 9]

Kodu buradan alın: https://github.com/uqfoundation/pathos

Ve yapabileceklerinden biraz daha fazlasını göstermek için:

>>> from pathos.multiprocessing import ProcessingPool as Pool
>>> 
>>> p = Pool(4)
>>> 
>>> def add(x,y):
...   return x+y
... 
>>> x = [0,1,2,3]
>>> y = [4,5,6,7]
>>> 
>>> p.map(add, x, y)
[4, 6, 8, 10]
>>> 
>>> class Test(object):
...   def plus(self, x, y): 
...     return x+y
... 
>>> t = Test()
>>> 
>>> p.map(Test.plus, [t]*4, x, y)
[4, 6, 8, 10]
>>> 
>>> res = p.amap(t.plus, x, y)
>>> res.get()
[4, 6, 8, 10]

1
patos.multiprocessing ayrıca amapilerleme çubuklarının ve diğer asenkron programların kullanılmasını sağlayan asenkron bir haritaya ( ) sahiptir.
Mike McKerns

Çok işlemciliğin tadını çıkarırken paralel olmayan haritanın neredeyse bir damla değiştirmesine hizmet edebilen patos.multiprocessing'i seviyorum. Patos.multiprocessing.map basit bir sarıcı var, böylece birden çok çekirdek arasında salt okunur bir büyük veri yapısı işlenirken daha fazla bellek tasarruflu, bu git deposuna bakın .
Fashandge

İlginç görünüyor, ancak yüklenmiyor. Could not find a version that satisfies the requirement pp==1.5.7-pathos (from pathos)
Pip'in

1
Evet. Ayrı bir pakete işlevselliği bölmek ve ayrıca 2/3 uyumlu koda dönüştürmek gibi bir süredir serbest bırakmadım. Yukarıdakilerin çoğu multiprocess, 2/3 uyumlu olan modülerleştirilmiştir . Bkz. Stackoverflow.com/questions/27873093/… ve pypi.python.org/pypi/multiprocess .
Mike McKerns

3
@xApple: Bir takip olarak pathosyeni bir kararlı sürüm yayınladı ve ayrıca 2.x ve 3.x uyumlu.
Mike McKerns

40

Bildiğim kadarıyla probleminize şu anda bir çözüm yok: Verdiğiniz işleve map()modülünüzün içe aktarılmasıyla erişilebilmelidir. Bu nedenle robert'in kodu çalışır: fonksiyon f()aşağıdaki kodu içe aktararak elde edilebilir:

def f(x):
    return x*x

class Calculate(object):
    def run(self):
        p = Pool()
        return p.map(f, [1,2,3])

if __name__ == '__main__':
    cl = Calculate()
    print cl.run()

Aslında bir "ana" bölüm ekledim, çünkü bu Windows platformu için önerileri takip ediyor ("Ana modülün istenmeyen yan etkilere neden olmadan yeni bir Python yorumlayıcısı tarafından güvenle alınabildiğinden emin olun").

Ayrıca Calculate, PEP 8'i takip etmek için önüne bir büyük harf ekledim . :)


18

Bayır tarafından çözüm doğrudur, ancak bir hata vardır: çocuk büyük miktarda veri geri gönderirse pipe.send(), ebeveyn çocuğun çıkmasını beklerken borunun tamponunu doldurabilir, çocuğun üzerinde tıkayabilir pipe.join(). Çözüm, çocuğu almadan önce çocuğun verilerini okumaktır join(). Ayrıca, çocuk bir kilitlenmeyi önlemek için ebeveynin borunun ucunu kapatmalıdır. Aşağıdaki kod bunu düzeltir. Bunun parmap, öğedeki her öğe için bir işlem oluşturduğunu da unutmayın X. Daha gelişmiş bir çözüm, bir dizi parçaya multiprocessing.cpu_count()bölmek Xve daha sonra geri dönmeden önce sonuçları birleştirmek için kullanmaktır. Bunu, güzel cevabın özlüğini mrule tarafından bozmamak için okuyucuya bir egzersiz olarak bırakıyorum. ;)

from multiprocessing import Process, Pipe
from itertools import izip

def spawn(f):
    def fun(ppipe, cpipe,x):
        ppipe.close()
        cpipe.send(f(x))
        cpipe.close()
    return fun

def parmap(f,X):
    pipe=[Pipe() for x in X]
    proc=[Process(target=spawn(f),args=(p,c,x)) for x,(p,c) in izip(X,pipe)]
    [p.start() for p in proc]
    ret = [p.recv() for (p,c) in pipe]
    [p.join() for p in proc]
    return ret

if __name__ == '__main__':
    print parmap(lambda x:x**x,range(1,5))

İşlem sayısını nasıl seçersiniz?
patapouf_ai

Ancak hata nedeniyle oldukça hızlı bir şekilde ölür OSError: [Errno 24] Too many open files. Bence düzgün çalışması için süreçlerin sayısı üzerinde bir çeşit sınır olması gerekir ...
patapouf_ai

13

Ben de bununla mücadele ettim. Basitleştirilmiş bir örnek olarak, bir sınıfın veri üyeleri olarak işlevleri vardı:

from multiprocessing import Pool
import itertools
pool = Pool()
class Example(object):
    def __init__(self, my_add): 
        self.f = my_add  
    def add_lists(self, list1, list2):
        # Needed to do something like this (the following line won't work)
        return pool.map(self.f,list1,list2)  

Ben aynı sınıf içinde bir Pool.map () çağrısında self.f işlevini kullanmak gerekiyordu ve self.f bağımsız değişken olarak bir tuple almadı. Bu işlev bir sınıfa gömülü olduğundan, diğer cevapların önerdiği sarıcı türünü nasıl yazacağım net değildi.

Ben ilk eleman işlevi ve kalan öğeleri eval_func_tuple (f_args) denilen bu işlevin bağımsız değişkenler olduğu bir tuple / list alır farklı bir sarmalayıcı kullanarak bu sorunu çözdü. Bunu kullanarak, sorunlu satır return pool.map (eval_func_tuple, itertools.izip (itertools.repeat (self.f), list1, list2)) ile değiştirilebilir. İşte tam kod:

Dosya: util.py

def add(a, b): return a+b

def eval_func_tuple(f_args):
    """Takes a tuple of a function and args, evaluates and returns result"""
    return f_args[0](*f_args[1:])  

Dosya: main.py

from multiprocessing import Pool
import itertools
import util  

pool = Pool()
class Example(object):
    def __init__(self, my_add): 
        self.f = my_add  
    def add_lists(self, list1, list2):
        # The following line will now work
        return pool.map(util.eval_func_tuple, 
            itertools.izip(itertools.repeat(self.f), list1, list2)) 

if __name__ == '__main__':
    myExample = Example(util.add)
    list1 = [1, 2, 3]
    list2 = [10, 20, 30]
    print myExample.add_lists(list1, list2)  

Main.py çalıştırmak [11, 22, 33] verecektir. Bunu geliştirmekten çekinmeyin, örneğin eval_func_tuple anahtar kelime argümanları alacak şekilde değiştirilebilir.

Başka bir notta, başka bir cevapta, "parmap" fonksiyonu, mevcut CPU sayısından daha fazla İşlem durumunda daha verimli hale getirilebilir. Aşağıda düzenlenen bir sürümü kopyalıyorum. Bu benim ilk yazım ve doğrudan orijinal cevabı düzenlemem gerekip gerekmediğinden emin değildim. Ayrıca bazı değişkenleri yeniden adlandırdım.

from multiprocessing import Process, Pipe  
from itertools import izip  

def spawn(f):  
    def fun(pipe,x):  
        pipe.send(f(x))  
        pipe.close()  
    return fun  

def parmap(f,X):  
    pipe=[Pipe() for x in X]  
    processes=[Process(target=spawn(f),args=(c,x)) for x,(p,c) in izip(X,pipe)]  
    numProcesses = len(processes)  
    processNum = 0  
    outputList = []  
    while processNum < numProcesses:  
        endProcessNum = min(processNum+multiprocessing.cpu_count(), numProcesses)  
        for proc in processes[processNum:endProcessNum]:  
            proc.start()  
        for proc in processes[processNum:endProcessNum]:  
            proc.join()  
        for proc,c in pipe[processNum:endProcessNum]:  
            outputList.append(proc.recv())  
        processNum = endProcessNum  
    return outputList    

if __name__ == '__main__':  
    print parmap(lambda x:x**x,range(1,5))         

8

Klaus se ve aganders3'ün cevabını aldım ve daha okunabilir ve tek bir dosyada saklanan belgelenmiş bir modül yaptım. Sadece projenize ekleyebilirsiniz. Hatta isteğe bağlı bir ilerleme çubuğu var!

"""
The ``processes`` module provides some convenience functions
for using parallel processes in python.

Adapted from http://stackoverflow.com/a/16071616/287297

Example usage:

    print prll_map(lambda i: i * 2, [1, 2, 3, 4, 6, 7, 8], 32, verbose=True)

Comments:

"It spawns a predefined amount of workers and only iterates through the input list
 if there exists an idle worker. I also enabled the "daemon" mode for the workers so
 that KeyboardInterupt works as expected."

Pitfalls: all the stdouts are sent back to the parent stdout, intertwined.

Alternatively, use this fork of multiprocessing: 
https://github.com/uqfoundation/multiprocess
"""

# Modules #
import multiprocessing
from tqdm import tqdm

################################################################################
def apply_function(func_to_apply, queue_in, queue_out):
    while not queue_in.empty():
        num, obj = queue_in.get()
        queue_out.put((num, func_to_apply(obj)))

################################################################################
def prll_map(func_to_apply, items, cpus=None, verbose=False):
    # Number of processes to use #
    if cpus is None: cpus = min(multiprocessing.cpu_count(), 32)
    # Create queues #
    q_in  = multiprocessing.Queue()
    q_out = multiprocessing.Queue()
    # Process list #
    new_proc  = lambda t,a: multiprocessing.Process(target=t, args=a)
    processes = [new_proc(apply_function, (func_to_apply, q_in, q_out)) for x in range(cpus)]
    # Put all the items (objects) in the queue #
    sent = [q_in.put((i, x)) for i, x in enumerate(items)]
    # Start them all #
    for proc in processes:
        proc.daemon = True
        proc.start()
    # Display progress bar or not #
    if verbose:
        results = [q_out.get() for x in tqdm(range(len(sent)))]
    else:
        results = [q_out.get() for x in range(len(sent))]
    # Wait for them to finish #
    for proc in processes: proc.join()
    # Return results #
    return [x for i, x in sorted(results)]

################################################################################
def test():
    def slow_square(x):
        import time
        time.sleep(2)
        return x**2
    objs    = range(20)
    squares = prll_map(slow_square, objs, 4, verbose=True)
    print "Result: %s" % squares

EDIT : @ alexander-mcfarlane önerisi ve bir test işlevi eklendi


ilerleme çubuğunuzla ilgili bir sorun ... Çubuk yalnızca iş yükünün işlemciler arasında ne kadar verimsiz bir şekilde bölündüğünü ölçer. İş yükü mükemmel şekilde bölünmüşse, tüm işlemciler join()aynı anda olacak 100%ve tqdmekranda tamamlanmış bir flaş alacaksınız . Yararlı olacağı tek zaman, her işlemcinin önyargılı bir iş yükü varsa
Alexander McFarlane

1
tqdm()çizgiyi sarmak için harekete geçin: result = [q_out.get() for _ in tqdm(sent)]ve çok daha iyi çalışıyor - bunu gerçekten takdir etse de büyük çaba + 1
Alexander McFarlane

Bu tavsiye için teşekkürler, deneyeceğim ve ardından cevabı güncelleyeceğim!
xApple

Yanıt güncellenir ve ilerleme çubuğu çok daha iyi çalışır!
xApple

8

Bunun 6 yıl önce sorulduğunu biliyorum, ancak yukarıdaki önerilerin bazıları korkunç derecede karmaşık olduğu için çözümümü eklemek istedim, ancak çözümüm aslında çok basitti.

Tek yapmam gereken bir yardımcı fonksiyon için pool.map () çağrısını sarmak oldu. Sınıf nesnesini yöntemin argümanları ile birlikte bir tuple olarak geçirme, bu biraz buna benziyordu.

def run_in_parallel(args):
    return args[0].method(args[1])

myclass = MyClass()
method_args = [1,2,3,4,5,6]
args_map = [ (myclass, arg) for arg in method_args ]
pool = Pool()
pool.map(run_in_parallel, args_map)

7

Sınıflarda tanımlanan işlevler (sınıflar içindeki işlevlerde bile) gerçekten turşu yapmaz. Ancak, bu işe yarar:

def f(x):
    return x*x

class calculate(object):
    def run(self):
        p = Pool()
    return p.map(f, [1,2,3])

cl = calculate()
print cl.run()

15
teşekkürler, ama sınıf dışında işlevi tanımlamak için biraz kirli buluyorum. Sınıf, verilen bir görevi yerine getirmek için gereken her şeyi bir araya getirmelidir.
Mermoz

3
@Memoz: "Sınıf ihtiyaç duyduğu her şeyi bir araya getirmeli" Gerçekten mi? Bunun birçok örneğini bulamıyorum. Sınıfların çoğu diğer sınıflara veya işlevlere bağlıdır. Neden bir sınıf bağımlılığına "kirli" diyoruz? Bağımlılığın nesi yanlış?
S.Lott

İşlev, varolan sınıf verilerini değiştirmemelidir - çünkü diğer işlemdeki sürümü değiştirir - böylece statik bir yöntem olabilir. Statik bir yöntem turşu yapabilirsiniz: stackoverflow.com/questions/1914261/… Veya bu önemsiz bir şey için bir lambda kullanabilirsiniz.
robert

6

Bu sorunun 8 yıl 10 ay önce sorulduğunu biliyorum, ancak size çözümümü sunmak istiyorum:

from multiprocessing import Pool

class Test:

    def __init__(self):
        self.main()

    @staticmethod
    def methodForMultiprocessing(x):
        print(x*x)

    def main(self):
        if __name__ == "__main__":
            p = Pool()
            p.map(Test.methodForMultiprocessing, list(range(1, 11)))
            p.close()

TestObject = Test()

Sınıfınızın işlevini statik bir yöntem haline getirmeniz yeterlidir. Ancak bir sınıf yöntemiyle de mümkündür:

from multiprocessing import Pool

class Test:

    def __init__(self):
        self.main()

    @classmethod
    def methodForMultiprocessing(cls, x):
        print(x*x)

    def main(self):
        if __name__ == "__main__":
            p = Pool()
            p.map(Test.methodForMultiprocessing, list(range(1, 11)))
            p.close()

TestObject = Test()

Python 3.7.3'te test edilmiştir


3

Klaus se'nin yöntemini değiştirdim, çünkü benim için küçük listelerle çalışırken, öğe sayısı ~ 1000 veya daha fazla olduğunda askıda kalacaktı. NoneDurma koşulu ile işleri birer birer itmek yerine , giriş kuyruğunu bir kerede yüklerim ve süreçlerin boşalana kadar işlenmesine izin veririm.

from multiprocessing import cpu_count, Queue, Process

def apply_func(f, q_in, q_out):
    while not q_in.empty():
        i, x = q_in.get()
        q_out.put((i, f(x)))

# map a function using a pool of processes
def parmap(f, X, nprocs = cpu_count()):
    q_in, q_out   = Queue(), Queue()
    proc = [Process(target=apply_func, args=(f, q_in, q_out)) for _ in range(nprocs)]
    sent = [q_in.put((i, x)) for i, x in enumerate(X)]
    [p.start() for p in proc]
    res = [q_out.get() for _ in sent]
    [p.join() for p in proc]

    return [x for i,x in sorted(res)]

Düzenleme: ne yazık ki şimdi benim sistemde bu hatayı çalıştırıyorum: Çok İşleme Kuyruğu maksimum boyut sınırı 32767 , umarım orada geçici çözümler yardımcı olacaktır.


1

Hatanın söylediği gibi mümkün Poololmadığı için nesneyi sınıftaki nesneler listesinden bir şekilde el ile yoksayarsanız, kodunuzu herhangi bir sorun olmadan çalıştırabilirsiniz pickle. Bunu aşağıdaki __getstate__fonksiyonla yapabilirsiniz ( buraya da bakınız). PoolNesne bulmaya çalışacağız __getstate__ve __setstate__fonksiyonları ve çalıştırdığınızda onu bulursa onları yürütmek map, map_asyncvb:

class calculate(object):
    def __init__(self):
        self.p = Pool()
    def __getstate__(self):
        self_dict = self.__dict__.copy()
        del self_dict['p']
        return self_dict
    def __setstate__(self, state):
        self.__dict__.update(state)

    def f(self, x):
        return x*x
    def run(self):
        return self.p.map(self.f, [1,2,3])

Sonra şunları yapın:

cl = calculate()
cl.run()

size çıktı verecektir:

[1, 4, 9]

Python 3.x yukarıdaki kodu test ettik ve çalışıyor.


0

Bu yaklaşımın alındığından emin değilim ama kullandığım bir çalışma:

from multiprocessing import Pool

t = None

def run(n):
    return t.f(n)

class Test(object):
    def __init__(self, number):
        self.number = number

    def f(self, x):
        print x * self.number

    def pool(self):
        pool = Pool(2)
        pool.map(run, range(10))

if __name__ == '__main__':
    t = Test(9)
    t.pool()
    pool = Pool(2)
    pool.map(run, range(10))

Çıktı şöyle olmalıdır:

0
9
18
27
36
45
54
63
72
81
0
9
18
27
36
45
54
63
72
81

0
class Calculate(object):
  # Your instance method to be executed
  def f(self, x, y):
    return x*y

if __name__ == '__main__':
  inp_list = [1,2,3]
  y = 2
  cal_obj = Calculate()
  pool = Pool(2)
  results = pool.map(lambda x: cal_obj.f(x, y), inp_list)

Sınıfın her farklı örneği için bu işlevi uygulamak istemeniz mümkündür. İşte bunun için çözüm

class Calculate(object):
  # Your instance method to be executed
  def __init__(self, x):
    self.x = x

  def f(self, y):
    return self.x*y

if __name__ == '__main__':
  inp_list = [Calculate(i) for i in range(3)]
  y = 2
  pool = Pool(2)
  results = pool.map(lambda x: x.f(y), inp_list)

0

İşte benim çözümüm, bence buradaki diğerlerinden biraz daha az hackish. Nightowl'un cevabına benzer.

someclasses = [MyClass(), MyClass(), MyClass()]

def method_caller(some_object, some_method='the method'):
    return getattr(some_object, some_method)()

othermethod = partial(method_caller, some_method='othermethod')

with Pool(6) as pool:
    result = pool.map(othermethod, someclasses)

0

Gönderen http://www.rueckstiess.net/research/snippets/show/ca1d7d90 ve http://qingkaikong.blogspot.com/2016/12/python-parallel-method-in-class.html

Harici bir işlev yapabilir ve sınıfın kendi nesnesiyle tohumlayabiliriz:

from joblib import Parallel, delayed
def unwrap_self(arg, **kwarg):
    return square_class.square_int(*arg, **kwarg)

class square_class:
    def square_int(self, i):
        return i * i

    def run(self, num):
        results = []
        results = Parallel(n_jobs= -1, backend="threading")\
            (delayed(unwrap_self)(i) for i in zip([self]*len(num), num))
        print(results)

VEYA joblib olmadan:

from multiprocessing import Pool
import time

def unwrap_self_f(arg, **kwarg):
    return C.f(*arg, **kwarg)

class C:
    def f(self, name):
        print 'hello %s,'%name
        time.sleep(5)
        print 'nice to meet you.'

    def run(self):
        pool = Pool(processes=2)
        names = ('frank', 'justin', 'osi', 'thomas')
        pool.map(unwrap_self_f, zip([self]*len(names), names))

if __name__ == '__main__':
    c = C()
    c.run()

0

Bu çok iyi bir çözüm olmayabilir ama benim durumumda bunu böyle çözüyorum.

from multiprocessing import Pool

def foo1(data):
    self = data.get('slf')
    lst = data.get('lst')
    return sum(lst) + self.foo2()

class Foo(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def foo2(self):
        return self.a**self.b   

    def foo(self):
        p = Pool(5)
        lst = [1, 2, 3]
        result = p.map(foo1, (dict(slf=self, lst=lst),))
        return result

if __name__ == '__main__':
    print(Foo(2, 4).foo())

selfSınıfımın özniteliklerine ve işlevlerine bu işlev aracılığıyla erişmem gerektiği için işlevime geçmek zorunda kaldım. Bu benim için çalışıyor. Düzeltmeler ve öneriler her zaman beklerim.


0

İşte python3'te çok işlemli Havuz kullanmak için yazdığım bir kazan plakası, özellikle testleri çalıştırmak için python3.7.7 kullanıldı. En hızlı koşumu kullanarak aldım imap_unordered. Sadece senaryonuzu takın ve deneyin. Sen kullanabilirsiniz timeitya da sadece time.time()sizin için en iyi çalıştığını anlamaya.

import multiprocessing
import time

NUMBER_OF_PROCESSES = multiprocessing.cpu_count()
MP_FUNCTION = 'starmap'  # 'imap_unordered' or 'starmap' or 'apply_async'

def process_chunk(a_chunk):
    print(f"processig mp chunk {a_chunk}")
    return a_chunk


map_jobs = [1, 2, 3, 4]

result_sum = 0

s = time.time()
if MP_FUNCTION == 'imap_unordered':
    pool = multiprocessing.Pool(processes=NUMBER_OF_PROCESSES)
    for i in pool.imap_unordered(process_chunk, map_jobs):
        result_sum += i
elif MP_FUNCTION == 'starmap':
    pool = multiprocessing.Pool(processes=NUMBER_OF_PROCESSES)
    try:
        map_jobs = [(i, ) for i in map_jobs]
        result_sum = pool.starmap(process_chunk, map_jobs)
        result_sum = sum(result_sum)
    finally:
        pool.close()
        pool.join()
elif MP_FUNCTION == 'apply_async':
    with multiprocessing.Pool(processes=NUMBER_OF_PROCESSES) as pool:
        result_sum = [pool.apply_async(process_chunk, [i, ]).get() for i in map_jobs]
    result_sum = sum(result_sum)
print(f"result_sum is {result_sum}, took {time.time() - s}s")

Yukarıdaki senaryoda imap_unorderedaslında benim için en kötü performansı sergiliyor. Kasanızı deneyin ve çalıştırmayı planladığınız makinede kıyaslayın. Ayrıca üzerinde okumak Süreç Havuzlar . Şerefe!

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.