多线程
在软件开发中经常会碰到在一个进程内部同时处理不同业务功能的场景,这可以通过多线程的方式解决。
在开发多线程软件时,经常碰到数据访问冲突问 题,也就是不同的线程可能会同时访问同一个数据。如果不对数据进行保护,就会导致在访问数据时无法在一个CPU指令周期内完成操作,这样就会出现数据访问异常。
因此,需要对多线程访问的数据进行加锁保护。
下面举个例子说明下
界面
使用Qt Designer 新建一个Dialog
再拖4个Push button进去,选中后点击上面按钮使其变为网关布局
保存得到ui文件, 再使用Scripts下的 pyside6-uic.exe -o 目标.py 保存的.ui 获得py的界面文件
单体类
// config.py
import types
class CSingleton(type):
_instance = None
def __call__(self, *args, **kwargs):
if self._instance is None:
self._instance = super.__call__(*args, **kwargs)
return self._instance
def singleton(cls, *args, **kwargs):
instances = {
}
def wrapper():
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return wrapper
@singleton
class CConfig:
nTeacherNumber = 0
nStudentNumber = 0
def __init__(self):
pass
def setTeacherNumber(self, n):
self.nTeacherNumber = n
def getTeacherNumber(self):
return self.nTeacherNumber
def setStudentNumber(self, n):
self.nStudentNumber = n
def getStudentNumber(self):
return self.nStudentNumber
接收数据线程CRecvThread
// recvthread.py
import sys
import os
from PySide6.QtCore import QMutex, QMutexLocker, QThread, QFile
from config import CConfig
class CRecvThread(QThread):
bWorking = False
bFinshed = True
mtxRunning = QMutex()
def __init__(self):
super(CRecvThread, self).__init__()
def run(self):
self.bFinshed = False
self.bWorking = True
workspace_dir = os.path.dirname(os.path.abspath(__file__))
strFileName = workspace_dir + '/test/recv.txt'
strContent = str()
config = CConfig()
while self.isWorking():
QThread.sleep(1)
file = open(strFileName, 'r')
strContent = file.read()
file.close()
print(strContent)
strList = strContent.split(",")
print(strList)
if 2 == len(strList):
config.setTeacherNumber(int(strList[0]))
print(config.getTeacherNumber())
config.setStudentNumber(int(strList[1]))
print(config.getStudentNumber())
self.bFinshed = True
def isWorking(self):
locker = QMutexLocker(self.mtxRunning)
return self.bWorking
def exitThread(self):
self.mtxRunning.lock()
self.bWorking = False
self.mtxRunning.unlock()
while not self.bFinshed:
QThread.msleep(10)
发送数据线程程CSendThread
// sendthread.py
import os
from PySide6.QtCore import QMutex, QMutexLocker, QThread, QFile
from config import CConfig
class CSendThread(QThread):
bWorking = False
bFinshed = True
mtxRunning = QMutex()
def __init__(self):
super(CSendThread, self).__init__()
def run(self):
self.bFinshed = False
self.bWorking = True
workspace_dir = os.path.dirname(os.path.abspath(__file__))
strFileName = workspace_dir + '/test/send.txt'
file = QFile(strFileName)
strContent = str()
config = CConfig()
while self.isWorking():
QThread.sleep(1)
strContent = 'teacher:{0}, student:{1}'.format(config.getTeacherNumber(), config.getStudentNumber())
if not file.open(QFile.WriteOnly|QFile.Truncate|QFile.Text):
continue
file.write(strContent.encode('UTF-8'))
file.close()
self.bFinshed = True
def isWorking(self):
locker = QMutexLocker(self.mtxRunning)
return self.bWorking
def exitThread(self):
self.mtxRunning.lock()
self.bWorking = False
self.mtxRunning.unlock()
while not self.bFinshed:
QThread.msleep(10)
启动、停止线程
// cdialog.py
import os
import sys
from PySide6.QtCore import QDir
from PySide6.QtWidgets import QDialog, QApplication
from recvthread import CRecvThread
from sendthread import CSendThread
from ui_CDialog import Ui_Dialog
class cDialog(QDialog, Ui_Dialog):
recvThread = CRecvThread()
sendThread = CSendThread()
def __init__(self, parent=None):
super(cDialog, self).__init__(parent)
self.setupUi(self)
self.setWindowTitle('多线程')
workspace_dir = os.path.dirname(os.path.abspath(__file__))
strDir = workspace_dir + '/test/'
dir = QDir()
if not dir.exists(strDir):
dir.mkpath(strDir)
self.btnStartThread.clicked.connect(self.slot_startthread)
self.btnStopThread.clicked.connect(self.slot_stopthread)
def __del__(self):
self.slot_stopthread()
def slot_startthread(self):
self.recvThread.start()
self.sendThread.start()
def slot_stopthread(self):
self.recvThread.exitThread()
self.sendThread.exitThread()
if __name__ == "__main__":
app = QApplication(sys.argv)
d = cDialog()
d.exec()
sys.exit(0)
启动
点击start后
总结
· 开发自定义线程类实现多线程开发,自定义线程类从
QThread派生。
· start()接口用来启动线程运行,需要重写线程类的 run()接口,以便实现业务功能。run()接口的主循环中应该根据工作标志判断是否需要继续执行循环。
· 编写exitThread()接口用来停止线程运行,在该接口内应该等待 run()接口退出运行。
· 使用QMutex保护数据,防止多线程访问数据时异常。可以
使用QMutexLocker简化对互斥锁的操作。