Python - Как читать экземпляры класса в реальном времени из отдельного процесса, который печатает информацию - PullRequest
0 голосов
/ 29 ноября 2018

У меня есть кусок кода, который постоянно создает новые экземпляры класса Car.При этом класс 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, эти экземпляры скрыты вдругой дочерний процесс пока отлично виден основному процессу.Я уверен, что мне нужно использовать что-то вроде очереди или трубы.Однако я не уверен, как.Это вывод вышеуказанного кода:

2
from main process 3
2
2
2
2
2

Ответы [ 2 ]

0 голосов
/ 29 ноября 2018

Если все, что вам нужно, это вести подсчет количества имеющихся у вас автомобилей, вы можете использовать объект общей памяти, например Значение .

Вы можете достичь того, что хотите, всего лишьНесколько изменений в вашем коде:

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
0 голосов
/ 29 ноября 2018

Справочная информация: Поскольку Python по своей природе является однопоточным (интерпретатор защищен GIL или глобальной блокировкой интерпретатора), в нем нет истинных потоков.Вместо этого, чтобы достичь того же эффекта, вы должны использовать разные процессы, как вы делаете в своем примере.Поскольку это разные процессы с разными интерпретаторами и разными пространствами имен, вы не сможете получить доступ к обычным данным в одном процессе из другого процесса.Когда вы создаете новый процесс, интерпретатор Python разветвляется и создает копию всех объектов Python, поэтому Car.car_list теперь представляет собой два разных объекта, по одному в каждом процессе.Поэтому, когда один процесс добавляет в этот список, он добавляет в другой список, чем другой процесс читает.

Ответ: Ваша догадка была правильной: вы захотите использовать один изструктуры данных в многопроцессорном модуле.Эти структуры данных специально написаны так, чтобы они были «потокобезопасными» (я полагаю, что в этом контексте «безопасными для процессов») и чтобы распределять общие данные между двумя процессами за кулисами.

Пример: вы можете использовать глобальную очередь, в которой процесс «производитель» добавляет элементы, а процесс «потребитель» удаляет их и добавляет их в свою собственную копию списка.

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, они удаляют ожидающие автомобили из очереди, и потребительский процесс не получит их.Если вам нужно иметь несколько пользовательских процессов, вам потребуется другой подход.

...