Python How to share a serial port with two different threads (Class A, Class B)

10,201

While you could share the serial port using appropriate locking, I wouldn't recommend it. I've written several multi-threaded applications that communicate on the serial port in Python, and in my experience the following approach is better:

  • Have a single class, in a single thread, manage the actual serial port communication, via a Queue object or two:
    • Stuff read from the port is placed into the queue
    • Commands to send to the port are placed into the queue and the "Serial thread" sends them
  • Have the other threads implement logic by placing things into the queue and taking things out

Using Queue objects will greatly simplify your code and make it more robust.

This approach opens a lot of possibilities for you in terms of design. You can, for example, register events (callbacks) with the serial thread manager and have it call them (in a synchronized way) when interesting events occur, etc.

Share:
10,201
Jaime
Author by

Jaime

Updated on June 15, 2022

Comments

  • Jaime
    Jaime almost 2 years

    I have a single Python process which is using a serial port (unique resource) which is managed using an instance of a class A. There exists two different threads initialized using instances of classes B and C, which are constantly using the serial port resource through the objected already created.

    import threading
    
    Class A(threading.Thread):
        #Zigbee serial port handler
        def __init__(self,dev):
            #something here that initialize serial port
        def run():
            while True:
                #listening serial interface
        def pack(self):
            #something
        def checksum(self):
            #something
        def write(self):
            #something
    
    Class B(threading.Thread):
        def __init__(self,SerialPortHandler):
            self.serialporthandler=SerialPortHandler
        def run(self)
            while True:
                #something that uses self.serialporthandler
    
    Class C(threading.Thread):
        def __init__(self,SerialPortHandler):
            self.serialporthandler=SerialPortHandler
        def run(self)
            while True:
                #something that uses self.serialporthandler
    
    def main():
        a=A('/dev/ttyUSB1')
        b=B(a)
        b.start()
        c=C(a)
        c.start()
    
    if __name__=='main':
        while True:
            main()
    

    The problem is that both threads are trying to access the serial resource at the same time. I could use several instances of the same class A, attaching Lock.acquire() and Lock.release() in the sensitive parts.

    Could some of you point me to the right way?

    Thank you in advance.

  • Jochen Ritzel
    Jochen Ritzel over 13 years
    Locks are good, it's just that in Python you use them like with self.lock: return something()
  • user470379
    user470379 over 13 years
    Assuming OP is using 2.6 or later.
  • Eli Bendersky
    Eli Bendersky over 13 years
    Beware of exceptions being thrown while you're holding the lock. Using with (Python 2.6+) here or try/finally would be prudent.
  • user470379
    user470379 over 13 years
    @singularity I didn't mean to cause any offense to you, but I felt that your answer still wasn't thread safe, which can be a very tricky thing to get right. If you feel my answer is incorrect, then by all means, please -1 and propose a correction. Thanks :)
  • Jaime
    Jaime over 13 years
    Thank you for the tip, I makes a lot of sense to me.