1、问题来源:
定义了一个Qthread进行网络任务,需要通过socket登陆后发送命令和接收回复消息。由于接收的可能是大文件,为了不阻塞网络,使用两个子线程进行接收和发送任务,大体代码如下:
import time import socket import inspect import ctypes import threading from queue import Queue from FrameDataSet import * from PyQt5.QtCore import QThread,pyqtSignal class TaskThread(QThread): sinout = pyqtSignal(dict) # 信号类型为字典,用于状态和数据 ConfigResvSingOut=pyqtSignal(dict) # 信号类型为字典,用于发送设置配置后收到的返回信息 def __init__(self): super(TaskThread, self).__init__() self.Frame = FrameData() #打包数据帧类 self.ConnectMark=False self.SendMessageQueue=Queue() #消息发送队例,先进先出队列 self.RecvMessageQueue=Queue() #消息接收队例,先进先出队列 self.SendMessageThread=threading.Thread(target=self.SendCmdMessage) #发送消息子线程 self.RecvMessageThread=threading.Thread(target=self.RecvCmdMessage) #接收消息子线程 def run(self): if self.TcpConnect(): #打开socket连接上网络 self.LoginInfo() #发送登陆 self.MessageThreadStart() #启动接收和发送消息线程 self.WarningHeartbeat() while self.ConnectMark: self.WarningHeartbeat() #心跳 self.ReturnMessageProcessing() #返回消息处理 def LoginInfo(self): #登陆 """函数代码""" def WarningHeartbeat(self): """函数代码""" def ReturnMessageProcessing(self):#返回消息处理函数 """函数代码""" def MessageThreadStart(self): #发送和接收线程启动 print("启用消息发送和接收线程") self.SendMessageThread.start() #启用命令发送子线程 self.RecvMessageThread.start() #启用命令接收子线程 def TcpConnect(self): #连接网络 try: self.tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.tcpSock.connect(self.Addr) print("尝试连接") self.ConnectMark = True return True except: print("尝试连接失败,关闭链接") self.CloseConnect() return False def CloseConnect(self): print("启用TaskThread CloseConnect关闭连接") self.ConnectMark = False self.SendMessageMark = False self.RecvMessageMark = False self.SendMessageThread.join() self.RecvMessageThread.join() self.tcpSock.close() print("链接已断开")
2、问题:
代码调用运行后,连接上后断开,再次连接,一直提示RuntimeError: threads can only be started once错误
3、问题原因
网上搜python线程相关资料,有个关于线程的说法是每个线程必须只能调用一次,也就是说只能start一次
而从我的定义来看,在初始化的时候就定义了线程,所以再次调用,这个线程就不能再被start了。
4、问题解决,将线程定义在启用线程的函数中,即:
import time
import socket
import inspect
import ctypes
import threading
from queue import Queue
from FrameDataSet import *
from PyQt5.QtCore import QThread,pyqtSignal
class TaskThread(QThread):
sinout = pyqtSignal(dict) # 信号类型为字典,用于状态和数据
ConfigResvSingOut=pyqtSignal(dict) # 信号类型为字典,用于发送设置配置后收到的返回信息
def __init__(self):
super(TaskThread, self).__init__()
self.Frame = FrameData() #打包数据帧类
self.ConnectMark=False
self.SendMessageQueue=Queue() #消息发送队例,先进先出队列
self.RecvMessageQueue=Queue() #消息接收队例,先进先出队列
def run(self):
if self.TcpConnect(): #打开socket连接上网络
self.LoginInfo() #发送登陆
self.MessageThreadStart() #启动接收和发送消息线程
self.WarningHeartbeat()
while self.ConnectMark:
self.WarningHeartbeat() #心跳
self.ReturnMessageProcessing() #返回消息处理
def LoginInfo(self): #登陆
"""函数代码"""
def WarningHeartbeat(self):
"""函数代码"""
def ReturnMessageProcessing(self):#返回消息处理函数
"""函数代码"""
def MessageThreadStart(self): #发送和接收线程启动
print("启用消息发送和接收线程")
self.SendMessageThread=threading.Thread(target=self.SendCmdMessage) #发送消息子线程,不能在初始化时定义,否则会出现RuntimeError错误
self.RecvMessageThread=threading.Thread(target=self.RecvCmdMessage) #接收消息子线程,不能在初始化时定义,否则会出现RuntimeError错误
self.SendMessageThread.start() #启用命令发送子线程
self.RecvMessageThread.start() #启用命令接收子线程
def TcpConnect(self): #连接网络
try:
self.tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tcpSock.connect(self.Addr)
print("尝试连接")
self.ConnectMark = True
return True
except:
print("尝试连接失败,关闭链接")
self.CloseConnect()
return False
def CloseConnect(self):
print("启用TaskThread CloseConnect关闭连接")
self.ConnectMark = False
self.SendMessageMark = False
self.RecvMessageMark = False
self.SendMessageThread.join()
self.RecvMessageThread.join()
self.tcpSock.close()
print("链接已断开")