pythonmultiprocessingpython-multithreading

Python Multiprocessing Self-updating Object


I'm trying to write a display for cardinal directions. I've got an interface that runs at a high refresh rate, and a hardware device that only updates a few times a second.

I need to be able to get the latest reported values from the hardware to the display every time the display updates.

My current approach is to try multiprocessing. I have a process that gets the values and updates the object, via shared-state pipes, with those values, and I have a process that takes the values from the object and updates the display.

Unfortunately, it seems to be blocking somewhere, and the display is locked to the hardware update speed.

class AccMag():
    # Init manually to ensure access to and for internal functions
    def init(self):
        # This defines the hardware access
        self.i2c = busio.I2C(board.SCL, board.SDA)
        self.mag = adafruit_lis2mdl.LIS2MDL(self.i2c)
        self.accel = adafruit_lsm303_accel.LSM303_Accel(self.i2c)

        # Output values (mag#) and pipes to the other process
        self.magx = 0
        self.pipex = multiprocessing.Value('d', 0.0)
        self.magy = 0
        self.pipey = multiprocessing.Value('d', 0.0)
        self.magz = 0
        self.pipez = multiprocessing.Value('d', 0.0)

        # The process, named thread.  Yes, my cat is named Rex.
        self.thread = multiprocessing.Process(target=self.threadmagdate, args=(self.pipex, self.pipey, self.pipez))
        self.thread.start()

        # A counter I use to see how often the process has run.
        # Comparing this counter to a similar one in the display is how I know it's blocking.
        self.tc = 0

    # The function that starts the process to update the values
    def magdate(self):
        # Check the current process is done
        self.thread.join(timeout=0)
        if self.thread.is_alive() == False:
            # Pull out previous values
            self.magx = self.pipex.value
            self.magy = self.pipey.value
            self.magz = self.pipez.value
            self.tc += 1
            # Start a new process
            self.thread = multiprocessing.Process(target=self.threadmagdate, args=(self.pipex, self.pipey, self.pipez))
            self.thread.start()

    # Get the data from the hardware into the pipe
    def threadmagdate(self, magx, magy, magz,):
        magx.value = int(self.mag.magnetic[1])
        magy.value = int(self.mag.magnetic[0])
        magz.value = int(self.mag.magnetic[2])

The display can then call magdate() and grab the values from the mag# values.

I'm missing something obvious, I can smell it, but I've got no idea why it's locking.


Solution

  • Something like this should work with threads:

    class AccMag:
        # Init manually to ensure access to and for internal functions
        def __init__(self):
            # This defines the hardware access
            self.i2c = busio.I2C(board.SCL, board.SDA)
            self.mag = adafruit_lis2mdl.LIS2MDL(self.i2c)
            self.accel = adafruit_lsm303_accel.LSM303_Accel(self.i2c)
    
            self.mag_data = (0, 0, 0)
            self.update_count = 0
    
            self.thread = threading.Thread(target=self._mag_thread, daemon=True)
            self.thread.start()
    
        # Get the data from the hardware into the pipe
        def _mag_thread(self):
            while True:
                mag = tuple(map(int, self.mag.magnetic))
                self.mag_data = mag
                self.update_count += 1
    

    There's no need to manually call magdata with this implementation: just grab the x, y, z values from .mag_data directly.

    In your main display application, copy a reference to the mag_data tuple in a local variable (e.g. cur_data = accmag.mag_data; x = cur_data[0]) rather than accessing fields directly (e.g. x = accmag.mag_data[0]) in order to avoid racing with the update thread and seeing partial results.

    However, I suspect your blocking issue lies elsewhere, given that the other answer should exhibit zero blocking as well and yet seems to block for you.