Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/346.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在多处理应用异步中维护实例状态_Python_Oop_Python Multiprocessing - Fatal编程技术网

Python 在多处理应用异步中维护实例状态

Python 在多处理应用异步中维护实例状态,python,oop,python-multiprocessing,Python,Oop,Python Multiprocessing,我希望,如果我在一个实例方法中调用apply\u async,并得到它的结果,那么所做的任何更改都将成为分叉过程的一部分。然而,似乎每个应用异步的新调用都会创建所述实例的新副本 以下面的代码为例: 来自multiprocessing.pool导入池的 类别多重测试: 定义初始化(自): self.i=0 def运行(自): 将池(2)作为池: 工人职位=[] 对于范围(10)内的j: job=pool.apply\u async(self.process,(j,)) worker\u jobs.

我希望,如果我在一个实例方法中调用
apply\u async
,并得到它的结果,那么所做的任何更改都将成为分叉过程的一部分。然而,似乎每个应用异步的新调用都会创建所述实例的新副本

以下面的代码为例:

来自multiprocessing.pool导入池的

类别多重测试:
定义初始化(自):
self.i=0
def运行(自):
将池(2)作为池:
工人职位=[]
对于范围(10)内的j:
job=pool.apply\u async(self.process,(j,))
worker\u jobs.append(作业)
对于worker_作业中的作业:
res=job.get()
打印(“输入”,分辨率)
def流程(自我、inp):
打印(“i”,self.i)
self.i+=1
返回inp
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
mt=多重测试()
润山(
样本输出:

i 0
i 0
i 0
i 0
i 0
input 0
i 0
i 0
i 0
i 0
i 0
input 1
input 2
input 3
input 4
input 5
input 6
input 7
input 8
input 9
但由于我们有两个内核,其中10个输入分布在这两个内核上,所以我希望
I
属性会增加

我原以为会出现以下情况:

  • 主线程创建实例并调用
    run()
  • 主线程通过初始化两个新进程和原始Multitest实例(其中
    i=0
    )的副本,在池上分发
    apply\u async
    的工作
  • 对新进程多次调用
    process()
    (直到
    range()
    耗尽)。每次调用process时,该进程的self.i都会递增
注意:我不是在询问两个进程之间的共享状态。相反,我问的是为什么单个进程的类实例没有发生变化(为什么每个进程的
self.I
没有增加)

然而,我没有看到这种行为。相反,打印的输出仅为零,这表明我的期望是错误的:状态(属性
i
)没有维护,但每次调用
apply\u async
时都会创建一个新实例(或至少一个新副本)。我在这里遗漏了什么,我如何才能使这项工作如预期的那样成功?(最好使用
apply\u async
,尽管不是必需的。但应保持结果的顺序。)


据我所知,这种行为并不特定于
apply\u async
,也适用于其他
pool
方法。我有兴趣了解为什么会发生这种情况,以及如何将行为改变为我想要实现的行为。Bounty找到了可以为这两个问题提供答案的答案。

我想向您指出参考资料,但我还没有任何参考资料,因此我将根据经验证据分享我的想法:

每次调用apply_async都会准备名称空间的新副本。您可以通过在流程内部添加对
print(self)
的调用来看到这一点。所以这部分不是真的:

主线程分配工作。。。通过初始化两个新进程和 原始Multitest实例的副本

相反,有两个新进程和原始Multitest实例的十个副本。所有这些副本都是从主进程生成的,主进程的i副本没有增加。为了证明这一点,添加
time.sleep(1);self.i+=1
在调用apply\u async之前,请注意a)主线程中i的值递增,b)通过延迟for循环,在下一次调用apply\u async触发新副本时,原始多测试实例已更改

代码:

结果:

Creating new Multitest instance: <__main__.Multitest object at 0x1056fc8b0>
i 1
Copied instance: <__mp_main__.Multitest object at 0x101052d90>
i 2
Copied instance: <__mp_main__.Multitest object at 0x101052df0>
i 3
Copied instance: <__mp_main__.Multitest object at 0x101052d90>
input 0
input 1
input 2
i 4
Copied instance: <__mp_main__.Multitest object at 0x101052df0>
input 3
Creating new Multitest instance: <__main__.Multitest object at 0x1083f3880>
i 0 (pid: 3460)
Copied instance: <__mp_main__.Multitest object at 0x10d89cdf0>
i 0 (pid: 3463)
Copied instance: <__mp_main__.Multitest object at 0x10d89ce50>
Copied instance: <__mp_main__.Multitest object at 0x10550adf0>
i 0 (pid: 3462)
Copied instance: <__mp_main__.Multitest object at 0x10550ae50>
i 1 (pid: 3462)
i 1 (pid: 3463)
input 0
input 1
input 2
input 3
结果:

Creating new Multitest instance: <__main__.Multitest object at 0x1056fc8b0>
i 1
Copied instance: <__mp_main__.Multitest object at 0x101052d90>
i 2
Copied instance: <__mp_main__.Multitest object at 0x101052df0>
i 3
Copied instance: <__mp_main__.Multitest object at 0x101052d90>
input 0
input 1
input 2
i 4
Copied instance: <__mp_main__.Multitest object at 0x101052df0>
input 3
Creating new Multitest instance: <__main__.Multitest object at 0x1083f3880>
i 0 (pid: 3460)
Copied instance: <__mp_main__.Multitest object at 0x10d89cdf0>
i 0 (pid: 3463)
Copied instance: <__mp_main__.Multitest object at 0x10d89ce50>
Copied instance: <__mp_main__.Multitest object at 0x10550adf0>
i 0 (pid: 3462)
Copied instance: <__mp_main__.Multitest object at 0x10550ae50>
i 1 (pid: 3462)
i 1 (pid: 3463)
input 0
input 1
input 2
input 3
创建新的多测试实例:
I0(pid:3460)
复制的实例:
I0(pid:3463)
复制的实例:
复制的实例:
I0(pid:3462)
复制的实例:
i 1(pid:3462)
i 1(pid:3463)
输入0
输入1
输入2
投入3

此技术来自于

多处理和线程之间的一个区别是,在创建进程后,它使用的内存实际上是从其父进程克隆而来的,因此进程之间没有共享内存

以下是一个例子:

导入操作系统
导入时间
从线程导入线程
全局计数器=0
定义我的线程()
全局计数器
打印(“在线程中,全局\u计数器为%r,添加一个。”%global\u计数器)
全局计数器+=1
def测试_线程():
全局计数器
th=线程(目标=我的线程)
th.start()
加入
打印(“在父线程、子线程连接中,全局\u计数器现在是%r。”%global\u计数器)
def test_fork():
全局计数器
pid=os.fork()
如果pid==0:
打印(“在子进程中,全局\u计数器为%r,添加一个。”%global\u计数器)
全局计数器+=1
退出()
时间。睡眠(1)
打印(“在父进程中,子进程已死亡,全局\u计数器仍然是%r.%global\u计数器)
def main():
测试_线程()
测试叉()
如果名称=“\uuuuu main\uuuuuuuu”:
main()
输出:

in thread, global_counter is 0, add one.
in parent, child thread joined, global_counter is 1 now.
in child process, global_counter is 1, add one.
in parent, child process died, global_counter is still 1.
original instance is <__main__.Multitest object at 0x1072828d0>
pickle duplicates the instance, new instance is <__main__.Multitest object at 0x107283110>
就你而言:

for j in range(10):
    # Before fork, self.i is 0, fork() dups memory, so the variable is not shared to the child.
    job = pool.apply_async(self.process, (j,))
    # After job finishes, child's self.i is 1 (not parent's), this variable is freed after child dies.
    worker_jobs.append(job)
编辑: 在python3中,绑定方法也会包括对象本身,本质上是复制它。因此,每次调用
apply\u async
时,对象
self
也会被pickle

import os
from multiprocessing.pool import Pool
import pickle

class Multitest:
    def __init__(self):
        self.i = "myattr"

    def run(self):
        with Pool(2) as pool:
            worker_jobs = []
            for j in range(10):
                job = pool.apply_async(self.process, (j,))
                worker_jobs.append(job)

            for job in worker_jobs:
                res = job.get()
                print("input", res)

    def process(self, inp):
        print("i", self.i)
        self.i += "|append"

        return inp

def test_pickle():
    m = Multitest()
    print("original instance is %r" % m)

    pickled_method = pickle.dumps(m.process)
    assert b"myattr" in pickled_method

    unpickled_method = pickle.loads(pickled_method)
    # get instance from it's method (python 3)
    print("pickle duplicates the instance, new instance is %r" % unpickled_method.__self__)

if __name__ == '__main__':
    test_pickle()
输出:

in thread, global_counter is 0, add one.
in parent, child thread joined, global_counter is 1 now.
in child process, global_counter is 1, add one.
in parent, child process died, global_counter is still 1.
original instance is <__main__.Multitest object at 0x1072828d0>
pickle duplicates the instance, new instance is <__main__.Multitest object at 0x107283110>
原始实例是
pickle复制实例,新实例为

我相信以下情况正在发生:

  • 每次调用
    self.process
    ,方法都会被序列化(pickle)并发送到子进程。每次都会创建一个新副本
  • 该方法在子进程中运行,但由于它是单独副本的一部分,与父进程中的原始副本不同,因此其更改的状态不会也不能影响父进程。传回的唯一信息是返回值(也是pickle)
  • 请注意,子进程没有自己的
    Multitest
    实例,因为只有当
    \uuuuuu name\uuuuuu=='\uuuuuu main\uuuuu'
    不适用于池创建的分叉时,才会创建该实例

    如果要在child pro中保持状态