çoklu işlem: Bir dikteyi birden çok süreç arasında nasıl paylaşırım?


113

Birleştirilebilir kuyrukta çalışan birkaç işlem oluşturan Qve sonunda Dsonuçları depolamak için genel bir sözlüğü değiştirebilen bir program . (böylece her alt süreç Dkendi sonucunu depolamak ve diğer alt süreçlerin hangi sonuçları ürettiğini görmek için kullanabilir)

Bir çocuk süreçte sözlüğü D yazdırırsam, üzerinde yapılan değişiklikleri görüyorum (yani D'de). Ama ana süreç Q'ya katıldıktan sonra, eğer D'yi basarsam, bu boş bir sözdür!

Bunun bir senkronizasyon / kilit sorunu olduğunu anlıyorum. Biri bana burada neler olduğunu ve D'ye erişimi nasıl senkronize edebileceğimi söyleyebilir mi?


1
Bu, osx 10.14.4 kullanan python 3.7.2'de beklendiği gibi çalışmaz Dict senkronize edilmez ve içeriği diğer işlemler tarafından yeniden yazılır. Ancak <code> multiprocessing.Manager (). List () </code> beklendiği gibi çalışıyor.
Andrew Druchenko

Yanıtlar:


162

Genel bir cevap, bir Managernesneyi kullanmayı içerir . Dokümanlardan uyarlanmıştır:

from multiprocessing import Process, Manager

def f(d):
    d[1] += '1'
    d['2'] += 2

if __name__ == '__main__':
    manager = Manager()

    d = manager.dict()
    d[1] = '1'
    d['2'] = 2

    p1 = Process(target=f, args=(d,))
    p2 = Process(target=f, args=(d,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()

    print d

Çıktı:

$ python mul.py 
{1: '111', '2': 6}

4
Teşekkürler senderle. Aslında, D = multiprocessing.Manager (). Dict () sorunumu çözer. D = dict () kullanıyordum.
dop

3
@LorenzoBelli, yöneticiye erişimin senkronize olup olmadığını soruyorsanız, cevabın evet olduğuna inanıyorum. adı kadar ima eden multiprocessing.Manager()bir örnekSyncManager döndürür !
2017

@senderle Bir üst sürecin uyuşuk rasgele durumunu bir çocuk süreçle paylaşmak istiyorum. Kullanmayı denedim Managerama yine de şansım yok. Buradaki soruma bir göz atar ve bir çözüm önerebilir misiniz bir bakar mısınız? Rastgele bir sayı np.random.seed(None)ürettiğim her seferinde yaparsam, yine de farklı rastgele sayılar elde edebilirim , ancak bu benim istediğim şey olmayan üst sürecin rastgele durumunu kullanmama izin vermiyor. Herhangi bir yardım çok takdir edilmektedir.
Amir

1
@RadioControlled bir güncelleme yazmaktan mutluluk duyuyor, ancak kısaca, bunu doğrudan yapabileceğinizi düşünmüyorum, ancak aynı anahtarlar ve değerler ile kolayca yeni bir yönetilen dikte oluşturabilir ve orijinal yerine onu kullanabilirsiniz. Bu senin davan için yeterli mi?
gönderen

1
@senderle, sonunda bunu yaptım. Yani cevap, tam da bunu yapmak zorunda olmanız olacaktır.
Radyo Kontrollü

25

çoklu işlem, iş parçacığı gibi değildir. Her çocuk süreç, ana sürecin belleğinin bir kopyasını alır. Genel olarak durum, iletişim (kanallar / soketler), sinyaller veya paylaşılan hafıza yoluyla paylaşılır.

Çoklu işlem, kullanım durumunuz için bazı soyutlamalar sunar - proxy'ler veya paylaşılan bellek kullanımıyla yerel olarak değerlendirilen paylaşılan durum: http://docs.python.org/library/multiprocessing.html#sharing-state-between-processes

İlgili bölümler:


1
Çok teşekkürler. Beni / a çözümüne yönlendirdiniz: multiprocessing.Manager (). Dict ().
dop

"Her çocuk süreç, ana sürecin belleğinin bir kopyasını alacak" ifadesinin ne anlama geldiğini açıklayabilir mi?
Itsme2003

@ Itsme2003 varsayılan olarak, ortaya çıkan bir işlemin üst sürecin belleğine erişimi yoktur (bu, iş parçacıklarıyla ilgili önemli farklılıklardan biridir). Dolayısıyla, bir süreç üst sürecin bir nesnesine ihtiyaç duyduğunda, onun bir kopyasını oluşturmalıdır (gerçek nesneye bir referans almak yerine). Yukarıdaki cevap, nesnelerin süreçler arasında nasıl paylaşılacağını açıklamaktadır.
Niklas Mertsch

Çünkü bu genellikle yanlıştır: Nesneyi değiştirmediğiniz sürece, en azından normal Linux kurulumunda, nesne gerçekte bellekte yalnızca bir kez depolanacaktır. Değiştirilir değiştirilmez kopyalanacaktır. Hafızadan tasarruf etmeniz ve nesneyi değiştirmemeniz gerekiyorsa bu çok önemli olabilir.
Radyo Kontrollü

16

Yöneticinin diktesinden daha hızlı ve tonlarca bellek kullanan ve Mac OS için çalışmayan pyshmht kitaplığından daha basit ve daha kararlı olan kendi çalışmamı paylaşmak istiyorum. Gerçi benim diktim yalnızca düz dizeler için çalışıyor ve şu anda değişmez. Doğrusal problama uygulamasını kullanıyorum ve anahtar ve değer çiftlerini tablodan sonra ayrı bir bellek bloğunda depoluyorum.

from mmap import mmap
import struct
from timeit import default_timer
from multiprocessing import Manager
from pyshmht import HashTable


class shared_immutable_dict:
    def __init__(self, a):
        self.hs = 1 << (len(a) * 3).bit_length()
        kvp = self.hs * 4
        ht = [0xffffffff] * self.hs
        kvl = []
        for k, v in a.iteritems():
            h = self.hash(k)
            while ht[h] != 0xffffffff:
                h = (h + 1) & (self.hs - 1)
            ht[h] = kvp
            kvp += self.kvlen(k) + self.kvlen(v)
            kvl.append(k)
            kvl.append(v)

        self.m = mmap(-1, kvp)
        for p in ht:
            self.m.write(uint_format.pack(p))
        for x in kvl:
            if len(x) <= 0x7f:
                self.m.write_byte(chr(len(x)))
            else:
                self.m.write(uint_format.pack(0x80000000 + len(x)))
            self.m.write(x)

    def hash(self, k):
        h = hash(k)
        h = (h + (h >> 3) + (h >> 13) + (h >> 23)) * 1749375391 & (self.hs - 1)
        return h

    def get(self, k, d=None):
        h = self.hash(k)
        while True:
            x = uint_format.unpack(self.m[h * 4:h * 4 + 4])[0]
            if x == 0xffffffff:
                return d
            self.m.seek(x)
            if k == self.read_kv():
                return self.read_kv()
            h = (h + 1) & (self.hs - 1)

    def read_kv(self):
        sz = ord(self.m.read_byte())
        if sz & 0x80:
            sz = uint_format.unpack(chr(sz) + self.m.read(3))[0] - 0x80000000
        return self.m.read(sz)

    def kvlen(self, k):
        return len(k) + (1 if len(k) <= 0x7f else 4)

    def __contains__(self, k):
        return self.get(k, None) is not None

    def close(self):
        self.m.close()

uint_format = struct.Struct('>I')


def uget(a, k, d=None):
    return to_unicode(a.get(to_str(k), d))


def uin(a, k):
    return to_str(k) in a


def to_unicode(s):
    return s.decode('utf-8') if isinstance(s, str) else s


def to_str(s):
    return s.encode('utf-8') if isinstance(s, unicode) else s


def mmap_test():
    n = 1000000
    d = shared_immutable_dict({str(i * 2): '1' for i in xrange(n)})
    start_time = default_timer()
    for i in xrange(n):
        if bool(d.get(str(i))) != (i % 2 == 0):
            raise Exception(i)
    print 'mmap speed: %d gets per sec' % (n / (default_timer() - start_time))


def manager_test():
    n = 100000
    d = Manager().dict({str(i * 2): '1' for i in xrange(n)})
    start_time = default_timer()
    for i in xrange(n):
        if bool(d.get(str(i))) != (i % 2 == 0):
            raise Exception(i)
    print 'manager speed: %d gets per sec' % (n / (default_timer() - start_time))


def shm_test():
    n = 1000000
    d = HashTable('tmp', n)
    d.update({str(i * 2): '1' for i in xrange(n)})
    start_time = default_timer()
    for i in xrange(n):
        if bool(d.get(str(i))) != (i % 2 == 0):
            raise Exception(i)
    print 'shm speed: %d gets per sec' % (n / (default_timer() - start_time))


if __name__ == '__main__':
    mmap_test()
    manager_test()
    shm_test()

Dizüstü bilgisayarımda performans sonuçları:

mmap speed: 247288 gets per sec
manager speed: 33792 gets per sec
shm speed: 691332 gets per sec

basit kullanım örneği:

ht = shared_immutable_dict({'a': '1', 'b': '2'})
print ht.get('a')

14
Github? Belgeler? bu aracı nasıl kullanabiliriz?
Pavlos Panteliadis

10

@ Senderle'ın yanı sıra, bazıları da işlevinin nasıl kullanılacağını merak ediyor olabilir multiprocessing.Pool.

Güzel olan şey, örnek için üst seviyenin tüm tanıdık API'sini taklit eden bir .Pool()yöntem managerolmasıdır multiprocessing.

from itertools import repeat
import multiprocessing as mp
import os
import pprint

def f(d: dict) -> None:
    pid = os.getpid()
    d[pid] = "Hi, I was written by process %d" % pid

if __name__ == '__main__':
    with mp.Manager() as manager:
        d = manager.dict()
        with manager.Pool() as pool:
            pool.map(f, repeat(d, 10))
        # `d` is a DictProxy object that can be converted to dict
        pprint.pprint(dict(d))

Çıktı:

$ python3 mul.py 
{22562: 'Hi, I was written by process 22562',
 22563: 'Hi, I was written by process 22563',
 22564: 'Hi, I was written by process 22564',
 22565: 'Hi, I was written by process 22565',
 22566: 'Hi, I was written by process 22566',
 22567: 'Hi, I was written by process 22567',
 22568: 'Hi, I was written by process 22568',
 22569: 'Hi, I was written by process 22569',
 22570: 'Hi, I was written by process 22570',
 22571: 'Hi, I was written by process 22571'}

Bu, her işlemin yalnızca işlem kimliğini global DictProxynesneye kaydettiği biraz farklı bir örnektir d.


3

Belki pyshmht'i deneyebilirsin Python için bellek tabanlı karma tablo uzantısını paylaşan .

Farkına varmak

  1. Tam olarak test edilmedi, sadece referansınız için.

  2. Şu anda çoklu işlem için kilit / sem mekanizmalarından yoksundur.

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.