在 Python 中使用线程通常涉及到 threading 模块,这是一个用于创建和管理线程的强大工具。如果你的类需要在创建的每个实例中运行一个线程,你可以在类中定义线程的行为,并在类的初始化方法中启动线程。

Python中的导入类中运行线程_主线程

1、问题背景

在一个项目中,需要使用一个 GSM900 调制解调器和一个树莓派来进行通信。为了方便使用,创建了一个包含相关功能的库 Serialworker.py。在主 Python 应用程序(sniffer.py)中导入该库并使用 serialworker 类中的 start() 函数时,遇到了一个问题:start() 函数一运行,代码就会阻塞,导致无法继续执行后面的代码。

2、解决方案

经过调查,发现问题的原因在于 start() 函数启动了一个线程,而该线程与主线程争用共享资源,导致主线程无法继续执行。为了解决这个问题,需要在 start() 函数中使用适当的锁或条件变量来同步线程之间的访问。

具体代码如下:

class serialworker():
    """This Class instantiates a serial thread and has functions for facilitating communication
    to the serial interface as well as some GSM900 specific functions"""

    def __init__(self, port='/dev/ttyAMA0', baud=115200, timeout=5, pollspeed=0.1):
        """Initialises the variables used in this class, baud=, timeout= and pollspeed= can be adjusted
        But have initial values of 115200, 5 and 0.1 respectively, in the future i could add stopbits and parity here"""

        self.port = port
        self.baud = baud
        self.timeout = timeout
        self.pollspeed = pollspeed
        self.run = False
        self.ser = serial.Serial

        """Command variable description:
        self.command        = Command to write in next while cycle
        self.commandwait    = Bool used to determine if write buffer contains a command for writing in the next while cycle
        self.commandstat    = Variable used to hold the return status of the command that was submitted
        self.commandret     = Bool used to determine if a command was written to the buffer, as to allow the output to be read in the next while cycle
        self.respwait       = Bool used to determine if commandstat is ready to be read
        """
        self.command = ""
        self.commandwait = False
        self.commandstat = ""
        self.commandret = False
        self.respwait = False
        self.lock = threading.Lock()

    def start(self):
        """Starts a thread of the _readwriteloop() funtion"""
        #TODO add thread checking, only 1 thread per serial interface should be allowed

        self.run = True
        t1 = threading.Thread(target=self.readwriteloop())
        t1.start()
        print("started")

    def checkgsm900online(self):
        """Checks if the GSM900 board is online"""
        with self.lock:
            gsmser = serial.Serial(
            port=self.port,
            baudrate=self.baud,
            parity=serial.PARITY_NONE,
            stopbits=serial.STOPBITS_ONE,
            bytesize=serial.EIGHTBITS,
            timeout=self.timeout,)
            gsmcheck = ("AT"+"\r\n").encode()
            gsmser.write(gsmcheck)
            sleep(0.03)
            gsmbuffer = gsmser.inWaiting()
            if gsmbuffer == 0:
                gsmser.flush()
                gsmser.close()
                return False
            else:
                gsmser.flush()
                gsmser.close()
                return True

    def check(self):
        """Checks if a thread currently exists"""

        return self.run

    def stop(self):
        """stops running thread"""

        with self.lock:
            self.run = False

    def inputcommand(self, string, flag):
        """Allows for a single command to be inputted, sets the commandwait to true to tell the loop to check its queue.
        Optionally you can set flag=False to not wait for a return stat on this command"""

        with self.lock:
            self.command = (string+"\r\n").encode()
            self.commandwait = True
            print("waiting for resp")
            if flag:
                while not self.respwait:
                    sleep(0.1)
                self.respwait = False
                return self.commandstat

    def readwriteloop(self):
        """Main function of the class that reads serial data from a buffer
        And checks for new commands inputted by the inputcommand() function
        which output status it will write to the commandstat buffer"""
        #TODO write function for retrieving input command return data not just the status

        ser = self.ser(
        port=self.port,
        baudrate=self.baud,
        parity=serial.PARITY_NONE,
        stopbits=serial.STOPBITS_ONE,
        bytesize=serial.EIGHTBITS,
        timeout=self.timeout,)
        while self.run:
            with self.lock:
                ser.inWaiting()
                buffer = ser.inWaiting()
                if buffer != 0:
                    decodeline = ser.readline().decode('utf-8', "ignore").rstrip('\n')
                    if len(decodeline) > 2:
                        if self.commandret:
                            if 'ERROR' in decodeline:
                                self.commandstat = decodeline
                                self.commandret = False
                                self.respwait = True
                            elif 'OK' in decodeline:
                                self.commandstat = decodeline
                                self.commandret = False
                                self.respwait = True
                            elif 'NO CARRIER' in decodeline:
                                self.commandstat = decodeline
                                self.commandret = False
                                self.respwait = True

                if self.commandwait:
                    ser.write(self.command)
                    #print(self.command)
                    self.commandwait = False
                    self.commandret = True
            sleep(self.pollspeed)

在更新后的代码中,在 start() 函数和 checkgsm900online() 函数中添加了锁。这样,在访问共享资源时,线程就会被同步,从而避免了争用问题。现在,start() 函数将不再阻塞主线程,代码可以正常运行。

要注意的是,在 Python 中使用锁时,必须确保在所有可能导致死锁的地方释放锁。在上面的代码中,锁只在 start() 函数和 checkgsm900online() 函数中使用,因此不会出现死锁问题。如果需要在代码的其他部分使用锁,则必须确保在适当的地方释放锁。

通过这种方式,你可以在 Python 中有效地使用面向对象的方法来管理多线程任务,提高程序的并发性能和响应速度。