Python - 如何从正在打印信息的单独进程实时读取 class 个实例
Python - How to read a class instances in real time from a separate process that is printing the information
我有一段代码不断创建 class Car 的新实例。这样做时,class Car 正在创建一个自身实例列表,因此当我想获取当前实例的信息时,我可以轻松地这样做,就像下面的代码:
from multiprocessing import Process
import time
class Car:
car_list = list()
def __init__(self, id, model):
self.id = id
self.model = model
Car.car_list.append(self)
@classmethod
def get_current_instances(cls):
return Car.car_list
class Interface:
def print_current_system(self):
while True:
print(len(Car.get_current_instances()))
time.sleep(1)
if __name__ == "__main__":
interface = Interface()
model = ["Toyota", "BMW"]
[Car(i, model[i]) for i in range(len(model))]
print_process = Process(target=interface.print_current_system)
print_process.start()
Car(2345, "Tesla")
print("from main process " + str(len(Car.get_current_instances())))
为了问题的目的,简化了此代码。但是,问题仍然存在。我正在从新进程调用函数 print_current_system。此函数不断查看 Car 的所有当前实例并打印汽车数量。
当我启动这个进程,然后添加一些新的 Car 实例时,这些实例对其他子进程是隐藏的,而对主进程是完全可见的。我很确定我需要使用诸如 Queue 或 Pipe 之类的东西。但是,我不确定如何。
这是上面代码的输出:
2
from main process 3
2
2
2
2
2
背景:因为Python本质上是单线程的(解释器由GIL或全局解释器锁保护),没有真正的线程在里面。相反,要达到相同的效果,您必须使用不同的过程,就像您在示例中所做的那样。因为这些是不同的进程,具有不同的解释器和不同的命名空间,所以您将无法从一个不同的进程访问一个进程中的正常数据。当您创建新进程时,python 解释器会分叉自身并复制所有 Python 对象,因此 Car.car_list 现在是两个不同的对象,每个进程一个。因此,当一个进程添加到该列表时,它正在添加到与另一个进程正在读取的列表不同的列表。
答案:您的直觉是正确的:您将要使用多处理模块中的一种数据结构。这些数据结构被专门编写为 "thread safe"(我猜实际上是 "process safe" 在这种情况下)并在幕后编组两个进程之间的共享数据。
示例: 您可以使用全局队列,其中 "producer" 进程添加项目,"consumer" 进程删除它们并将它们添加到自己的队列中列表副本。
from multiprocessing import Queue
class Car:
global_queue = Queue()
_car_list = [] # This member will be up-to-date in the producer
# process. In that process, access it directly.
# In the consumer process, call get_car_list instead.
# This can be wrapped in an interface which knows
# which process it is in, so the calling code does
# not have to keep track.
def __init__(self, id, model):
self.id = id
self.model = model
self.global_queue.put(self)
self._car_list.append(self)
@classmethod
def get_car_list(cls):
""" Get the car list for the consumer process
Note: do not call this from the producer process
"""
# Before returning the car list, pull all pending cars off the queue
# while cls.global_queue.qsize() > 0:
# qsize is not implemented on some unix systems
while not cls.global_queue.empty():
cls._car_list.append(cls.global_queue.get())
return cls._car_list
注意:使用上面的代码,你只能有一个数据消费者。如果其他进程调用 get_car_list 方法,它们将从队列中删除待处理的汽车,消费者进程将不会接收它们。如果您需要有多个消费者进程,则需要采用不同的方法。
如果你想要的只是计算你有多少辆汽车,你可以使用像 Value 这样的共享内存对象。
只需对代码进行一些更改即可实现您想要的结果:
from multiprocessing import Process, Value
import time
class Car:
car_list = list()
car_quantity = Value('i', 0) # Use a shared memory object here.
def __init__(self, id, model):
self.id = id
self.model = model
Car.car_list.append(self)
Car.car_quantity.value += 1 # Update quantity
@classmethod
def get_current_instances(cls):
return Car.car_list
class Interface:
def print_current_system(self):
while True:
print(Car.car_quantity.value) # Just print the value of the memory shared object (Value).
time.sleep(1)
if __name__ == "__main__":
interface = Interface()
model = ["Toyota", "BMW"]
[Car(i, model[i]) for i in range(len(model))]
print_process = Process(target=interface.print_current_system)
print_process.start()
time.sleep(3) # Added here in order you can see the
# ouptut changing from 2 to 3.
Car(2345, "Tesla")
print("from main process " + str(len(Car.get_current_instances())))
输出:
2
2
2
from main process 3
3
3
3
3
3
我有一段代码不断创建 class Car 的新实例。这样做时,class Car 正在创建一个自身实例列表,因此当我想获取当前实例的信息时,我可以轻松地这样做,就像下面的代码:
from multiprocessing import Process
import time
class Car:
car_list = list()
def __init__(self, id, model):
self.id = id
self.model = model
Car.car_list.append(self)
@classmethod
def get_current_instances(cls):
return Car.car_list
class Interface:
def print_current_system(self):
while True:
print(len(Car.get_current_instances()))
time.sleep(1)
if __name__ == "__main__":
interface = Interface()
model = ["Toyota", "BMW"]
[Car(i, model[i]) for i in range(len(model))]
print_process = Process(target=interface.print_current_system)
print_process.start()
Car(2345, "Tesla")
print("from main process " + str(len(Car.get_current_instances())))
为了问题的目的,简化了此代码。但是,问题仍然存在。我正在从新进程调用函数 print_current_system。此函数不断查看 Car 的所有当前实例并打印汽车数量。
当我启动这个进程,然后添加一些新的 Car 实例时,这些实例对其他子进程是隐藏的,而对主进程是完全可见的。我很确定我需要使用诸如 Queue 或 Pipe 之类的东西。但是,我不确定如何。 这是上面代码的输出:
2
from main process 3
2
2
2
2
2
背景:因为Python本质上是单线程的(解释器由GIL或全局解释器锁保护),没有真正的线程在里面。相反,要达到相同的效果,您必须使用不同的过程,就像您在示例中所做的那样。因为这些是不同的进程,具有不同的解释器和不同的命名空间,所以您将无法从一个不同的进程访问一个进程中的正常数据。当您创建新进程时,python 解释器会分叉自身并复制所有 Python 对象,因此 Car.car_list 现在是两个不同的对象,每个进程一个。因此,当一个进程添加到该列表时,它正在添加到与另一个进程正在读取的列表不同的列表。
答案:您的直觉是正确的:您将要使用多处理模块中的一种数据结构。这些数据结构被专门编写为 "thread safe"(我猜实际上是 "process safe" 在这种情况下)并在幕后编组两个进程之间的共享数据。
示例: 您可以使用全局队列,其中 "producer" 进程添加项目,"consumer" 进程删除它们并将它们添加到自己的队列中列表副本。
from multiprocessing import Queue
class Car:
global_queue = Queue()
_car_list = [] # This member will be up-to-date in the producer
# process. In that process, access it directly.
# In the consumer process, call get_car_list instead.
# This can be wrapped in an interface which knows
# which process it is in, so the calling code does
# not have to keep track.
def __init__(self, id, model):
self.id = id
self.model = model
self.global_queue.put(self)
self._car_list.append(self)
@classmethod
def get_car_list(cls):
""" Get the car list for the consumer process
Note: do not call this from the producer process
"""
# Before returning the car list, pull all pending cars off the queue
# while cls.global_queue.qsize() > 0:
# qsize is not implemented on some unix systems
while not cls.global_queue.empty():
cls._car_list.append(cls.global_queue.get())
return cls._car_list
注意:使用上面的代码,你只能有一个数据消费者。如果其他进程调用 get_car_list 方法,它们将从队列中删除待处理的汽车,消费者进程将不会接收它们。如果您需要有多个消费者进程,则需要采用不同的方法。
如果你想要的只是计算你有多少辆汽车,你可以使用像 Value 这样的共享内存对象。
只需对代码进行一些更改即可实现您想要的结果:
from multiprocessing import Process, Value
import time
class Car:
car_list = list()
car_quantity = Value('i', 0) # Use a shared memory object here.
def __init__(self, id, model):
self.id = id
self.model = model
Car.car_list.append(self)
Car.car_quantity.value += 1 # Update quantity
@classmethod
def get_current_instances(cls):
return Car.car_list
class Interface:
def print_current_system(self):
while True:
print(Car.car_quantity.value) # Just print the value of the memory shared object (Value).
time.sleep(1)
if __name__ == "__main__":
interface = Interface()
model = ["Toyota", "BMW"]
[Car(i, model[i]) for i in range(len(model))]
print_process = Process(target=interface.print_current_system)
print_process.start()
time.sleep(3) # Added here in order you can see the
# ouptut changing from 2 to 3.
Car(2345, "Tesla")
print("from main process " + str(len(Car.get_current_instances())))
输出:
2
2
2
from main process 3
3
3
3
3
3