PySide6学习专栏(四):用多线程完成复杂计算任务

news2025/2/22 5:42:15

如果计程序中要处理一个非常庞大的数据集中的数据,且数据处理计算很复杂,造成数据处理占用大量时间和CPU资源,如果不用多线程,仅在主进程中来处理数据,将会使整个程序卡死,必须采用多线程来处理这些数据是唯一的选择,下面分别给出几种多线程的使用方法供参考:

1、示例1:一采用基于PySide6的多线程(比PYTHON自带的多线程类要好用的多)来处理大数据的示例:

程序运行界面如下:

# -*- coding: utf-8 -*-
#模块功能:演示多线程类calcuteThread及其子类trdCalcute_01、trdCalcute_02....等来执行多线程数据计算,
#         用于将大量复杂的数据计算包装到多线程类中来完成
#         ID=1 ID=2两个线程演示多线程同主窗口数据通信,ID=3 ID=4演示用继承多线程基类calcuteThread
#         的子类trdCalcute_01、trdCalcute_02。。。来将大量复杂的数据分块后分别交由不同线程完成计算任务
#         一个线程类可初始化多个对象,对相同计算功能的可用一个同名线程类定义多个实例化线程对象,
#         每个线程对象执行相同的计算功能完成对拆分的大数据分块计算,不同计算功能的可分别继承定义不同功能的线程子类并实例化对象

import sys,os,time,math,copy,random
from math import * 
import PySide6
from PySide6 import *
from PySide6.QtCore  import *
#from PySide6.QtGui  import *
from PySide6.QtWidgets  import *
from PySide6.QtWidgets import (QApplication, QFrame, QHBoxLayout, QLabel,
    QLayout, QMainWindow, QMenu, QMenuBar,
    QPushButton, QSizePolicy, QStatusBar, QToolBar,
    QVBoxLayout, QWidget)
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
    QMetaObject, QObject, QPoint, QRect,
    QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QAction, QBrush, QColor, QConicalGradient,
    QCursor, QFont, QFontDatabase, QGradient,QMovie,
    QIcon, QImage, QKeySequence, QLinearGradient,
    QPainter, QPalette, QPixmap, QRadialGradient,
    QTransform)

import math         #此方法导入库,引用库中函数时,需加上模块前缀math.
from math import *  #此方法导入库,引入库中函数时,无需加上模块前缀math
import numpy as np  #此方法导入库,因采用了

MAX_THREAD_NUM=100   #最大允许可同时开的多线程数量

#基于PySide6的演示窗口类
class MainWindow(QWidget):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setWindowTitle('多线程(同时开4个)示例')
        self.resize(900,500)
        #仅示例,实际数据集可能几百万个,可设置干个线程,每个线程分别处理计算数据集中的某一段数据即可,最后将处理后的计算结果全并即完成任务
        self.lstDatas=[0,1,2,3,4,5,6,7,8,9]           #交由多线程(ID=3线程)计算处理的数据集合(可为字典,列表,np结构等,一般采用内存共享处理方式,多线程计算完成后,不必在主进程再对数据处理了)
        self.npDatas=np.array([0,1,2,3,4,5,6,7,8,9])  #交由多线程(ID=4线程)计算处理的np数据集合
        self.startTime3=self.endTime3=0.0  
        self.endTime4=self.endTime4=0.0 
        layout = QFormLayout()   
        self.pushButton_1 = QPushButton()
        self.pushButton_1.setText("开始新线程1(calcuteThread基类)")
        self.pushButton_2 = QPushButton()
        self.pushButton_2.setText("开始新线程2(calcuteThread基类)")
        self.pushButton_3 = QPushButton()
        self.pushButton_3.setText("开始线程3(calcuteThread的trdCalcute_01子类")
        self.pushButton_4 = QPushButton()
        self.pushButton_4.setText("停止线程1")
        self.pushButton_5 = QPushButton()
        self.pushButton_5.setText("停止线程2")
        self.pushButton_6 = QPushButton()
        self.pushButton_6.setText("停止线程3")
        self.pushButton_7 = QPushButton()
        self.pushButton_7.setText("开始线程4(calcuteThread的trdCalcute_01子类)")
        self.pushButton_8 = QPushButton()
        self.pushButton_8.setText("停止线程4")

        self.edita1 = QLineEdit()
        self.edita1.setText('信号槽:线程1的值')
        self.progressBar_1=QProgressBar()
        self.progressBar_1.setMinimum(0)
        self.progressBar_1.setMaximum(100)
        self.edita2 = QLineEdit()
        self.edita2.setText('信号槽:线程2的值')
        self.progressBar_2=QProgressBar()
        self.progressBar_2.setMinimum(0)
        self.progressBar_2.setMaximum(100)
        self.edita3 = QLineEdit()
        self.edita3.setText('信号槽:线程3的值')
        self.edita4 = QLineEdit()
        self.edita4.setText('信号槽:线程4的值')

        layout.addRow("单击按纽开始线程1(线程sleep 10毫秒)->",self.pushButton_1)
        layout.addRow("单击按纽结束线程1->",self.pushButton_4)
        layout.addRow("信号槽方式返回线程1的值->",self.edita1)
        layout.addRow("线程1返回值(进度条展示)->",self.progressBar_1)
        layout.addRow("单击按纽开始线程2(线程sleep 50毫秒)->",self.pushButton_2)
        layout.addRow("单击按纽结束线程2->",self.pushButton_5)
        layout.addRow("信号槽方式返回线程2的值->",self.edita2)
        layout.addRow("线程2返回值(进度条展示)->",self.progressBar_2)
        layout.addRow("单击按纽开始线程3(计算处理list数据集中的0-4段)->",self.pushButton_3)
        layout.addRow("单击按纽结束线程3->",self.pushButton_6)
        layout.addRow("信号槽方式返回线程3对list数据集(0-4段)计算处理结果->",self.edita3)
        layout.addRow("单击按纽开始线程4(计算处理np数据集中的5-9段)->",self.pushButton_7)
        layout.addRow("单击按纽结束线程4->",self.pushButton_8)
        layout.addRow("信号槽方式返回线程4对np数据集(5-9段)计算处理结果->",self.edita4)      
        
        self.setLayout(layout)
        self.pushButton_4.setEnabled(False)
        self.pushButton_5.setEnabled(False)
        self.pushButton_6.setEnabled(False)
        self.pushButton_8.setEnabled(False)

        #定义线程数组字典(相当于字典的key=线程ID号,字典的value=线程类的实例化对象)
        self.dicThread={}                                      #增加的线程数量不大于AX_THREAD_NUM,每增加一个线程,字典会相应增加线程变量
        self.bthreadopen=np.full((MAX_THREAD_NUM),False)       #定义线程打开的状况
        self.pushButton_1.clicked.connect(self.start_worker_1)
        self.pushButton_2.clicked.connect(self.start_worker_2)
        self.pushButton_3.clicked.connect(self.start_worker_3)
        self.pushButton_4.clicked.connect(self.stop_worker_1)
        self.pushButton_5.clicked.connect(self.stop_worker_2)
        self.pushButton_6.clicked.connect(self.stop_worker_3)
        self.pushButton_7.clicked.connect(self.start_worker_4)
        self.pushButton_8.clicked.connect(self.stop_worker_4)

    #点击按纽1的信号槽:开始线程1
    def start_worker_1(self):
        self.dicThread[1] = calcuteThread(1,None,0.01)
        self.bthreadopen[1]=True
        self.dicThread[1].start()
        self.dicThread[1].signal_ID.connect(self.threadToWindow)      #将线程1中的自定义信号signal_ID绑定槽函数self.dicThreadToWindow
        self.dicThread[1].signal_OK.connect(self.threadOK)            #将线程1中的自定义信号signal_OK绑定槽函数self.dicThreadOK
        self.pushButton_1.setEnabled(False)
        self.pushButton_4.setEnabled(True)
    #点击按纽2的信号槽:开始线程2
    def start_worker_2(self):
        self.dicThread[2] = calcuteThread(2,None,0.01)
        self.bthreadopen[2]=True
        self.dicThread[2].start()
        self.dicThread[2].signal_ID.connect(self.threadToWindow)    #将线程2中的自定义信号signal_ID绑定槽函数self.dicThreadToWindow
        self.pushButton_2.setEnabled(False)
        self.pushButton_5.setEnabled(True)
    #点击按纽3的信号槽:开始线程3,用于计算数据集self.lstDatas中的0-5段数据集,比如对一个几百上千万的大数据集,分解成10块数据,用10个线程分别计算分配的子块数据集,最后将各线程计算的数据集合并计算结果即可,避免在一个主进程中来计算全部数据
    def start_worker_3(self):
        self.startTime3=time.time()  #记录线程3从开始到计算完成用的时间
        self.edita3.setText('开始计算数据集self.lstDatas中的0-4段数据,请等待计算结果。。。。。。')
        lstDatas_3=[self.lstDatas,0,5]       #本线程处理计算lstDatas数据中的0-4号数据,因datas为列表,在多线程中对此self.lstDatas处理后的值已是处理后值了,要避免对同一区域数据不同的地方都在计算处理,引起错误
        self.dicThread[3] = trdCalcute_01(3,lstDatas_3)
        self.bthreadopen[3]=True
        self.dicThread[3].start()
        self.dicThread[3].signal_OK.connect(self.threadOK)           #将线程4中的自定义信号signal_OK绑定槽函数self.dicThreadOK
        self.pushButton_3.setEnabled(False)
        

    #点击按纽4的信号槽:开始线程4,用于计算数据集self.npDatas中的5-9段数据集
    def start_worker_4(self):
        self.startTime4=time.time()    #记录线程4从开始到计算完成用的时间
        self.edita4.setText('开始计算数据集self.npDatas中的5-9段数据,请等待计算结果。。。。。。')
        lstDatas_4=[self.npDatas,5,10]       #本线程处理计算npDatas数据中的5-9号数据,因npDatas为np数组,在多线程中对此self.npDatas处理后的值已是处理后值了,要避免对同一区域数据不同的地方都在计算处理,引起错误
        self.dicThread[4] = trdCalcute_01(4,lstDatas_4)
        self.bthreadopen[4]=True
        self.dicThread[4].start()
        self.dicThread[4].signal_OK.connect(self.threadOK)           #将线程4中的自定义信号signal_OK绑定槽函数self.dicThreadOK
        self.pushButton_7.setEnabled(False)

    #点击按纽4的信号槽:结束线程1
    def stop_worker_1(self):
        self.dicThread[1].bLooping=False
        time.sleep(0.5)
        self.dicThread[1].stop()
        self.pushButton_1.setEnabled(True)
        self.pushButton_4.setEnabled(False)
        self.bthreadopen[1]=False

    #点击按纽5的信号槽:结束线程2
    def stop_worker_2(self):
        self.dicThread[2].stop()
        self.pushButton_2.setEnabled(True)
        self.pushButton_5.setEnabled(False)
        self.bthreadopen[2]=False

    #点击按纽6的信号槽:结束线程3
    def stop_worker_3(self):
        self.dicThread[3].stop()
        self.pushButton_3.setEnabled(True)
        self.pushButton_6.setEnabled(False)
        self.bthreadopen[3]=False
        
        
    #点击按纽6的信号槽:结束线程4
    def stop_worker_4(self):
        self.dicThread[4].stop()
        self.pushButton_7.setEnabled(True)
        self.pushButton_8.setEnabled(False)
        self.bthreadopen[4]=False

    #绑定线程类中的signal_ID信号对应的槽函数,及时得到各线程中的变量一循环累加变量的值,用进度条形式体现出来
    #所有线程发送给主窗口的信号均绑定此槽函数,用ID号区分是哪个线程发的信号及值
    def threadToWindow(self,counter):
        ID = self.sender().ID           #在槽函数中被调用,用于获取发出信号的对象的索引号,等同于self.dicThread[n].ID
        ss=f'线程{ID}在多线程通过信号槽发送到主窗口的值={counter}'
        if ID == 1:
            self.progressBar_1.setValue(counter)
            self.edita1.setText(ss)
        elif ID == 2:
            self.progressBar_2.setValue(counter)
            self.edita2.setText(ss)
        #。。。。。。

     #所有线程发送signal_OK信号给主窗口均绑定此槽函数:判断用于分担计算的线程是否完成分配的全部计算工作
    def threadOK(self,perWork):    
        ID = self.sender().ID   #在槽函数中被调用,用于获取发出信号的对象的索引号,等同于self.dicThread[n].ID
        print(f'多线程{ID}计算完成百分比={perWork}%')
        perValue=int(perWork)  #因线程中定义的发信号函数参数为object,因实际发出的值类型是int,需要在此转换为int,如是字符串,转换用str(perWork)等,如是列表转换用list()等
        if ID == 3:
            if(perValue==100):
                self.endTime3=time.time()   #记录线程3计算完成时间
                self.edita3.setText(f'多线程{ID}中将数据0-5项值*10后={self.lstDatas}')
                reValue=self.dicThread[ID].getCalcuteValue()
                print(f'\n线程{ID}中变量计算值self.test={reValue}')
                print(f'线程3计算共用时={self.endTime3-self.startTime3}秒\n')
                self.pushButton_6.setEnabled(True)
        if ID == 4:
            if(perValue==100):
                self.endTime4=time.time()   #记录线程4计算完成时间
                self.edita4.setText(f'多线程{ID}中将数据5-9项值*20后={self.npDatas}') 
                reValue=self.dicThread[ID].getCalcuteValue()
                print(f'\n线程{ID}中变量计算值self.test={reValue}')
                print(f'线程4计算共用时={self.endTime4-self.startTime4}秒\n')
                self.pushButton_8.setEnabled(True)

        #。。。。。。 

#************************************************************************************************************************
#自定义计算用多线程类基类(继承PySide6的多线程类QtCore.QThread,不是PYTHON的线程类)
class calcuteThread(QtCore.QThread):
    signal_ID = Signal(int)     #示例:自定义线程中的信号,名称为signal_ID,用于向主窗体发送本线程的ID号,参数int,表示发出的信号为整数,参数类型可为int float str list object等
    signal_OK = Signal(object)  #向主窗体发送当前线程完成计算量的百分比,当为100时,表示线程计算任务完成,参数object表示发出的变量可作任意对象类型,如列表,字典,控件,窗体等等

    # 构造参数:      线程ID   计算用数据集合   线程中休眠时间  线程对应窗口        lstDatas格式=[数据集1,数据集2,......] 在调用处将数据集打包到此列表中供多线程使用,可多层嵌套列表、数组等
    def __init__(self,ID, lstDatas=None,sleepTime=0.01,parent=None):
        super(calcuteThread,self).__init__(parent)
        self.ID = ID                 #线程的ID号,在定义线程处的对象可以用self.sender().ID得到此值
        self.lstDatas=lstDatas       #用于多线程计算处理的全部数据集合(列表方式为内存共享,处理时需要注意主界面如也同时处理这些数据或其他线程也同时处理这些数据时的内容一致性问题)
        self.sleepTime = sleepTime   #线程计算过程中如使用sleep休眠的时间间隔
        
        self.bLooping= False         #此变量用于在run函数中控制可能用到的无限循环体,为False时,退出此无限循环体
        self.workOK=False            #本线程的数据处理工作是否全部完成
        self.workPersent=0           #本线程的数据处理工作完成的比例(0-100) 
        self.testN=0                 #测试用一自加的整数  

    #重载run函数:当在调用端调用线程的start()方法后,线程默认第一个被自动启动的函数即为run(),run执行完,并不表示线程已退出,要退出用线程对象.stop()
    def run(self):
        print(f'calcuteThread基类开始线程run函数,线程ID={self.ID}')
        self.bLooping= True         #用于控制无限循环体的开关,用无限循环处理一适合在循环体内处理的事务时,外部通过更改此开关变量为False时退出此无限循环(无限循环体中应使用time.sleep(秒)间隔代码,否则会造成主界面操作卡顿)
        while(self.bLooping):        
            self.testN+=1           #示例,测试用一自加1的变量 
            if self.testN==99: self.testN=0
            if(self.ID==1):
                pass               #对不同的线程ID如果有不同的处理内容
            elif(self.ID==2):
                pass     
            #......          
            time.sleep(self.sleepTime)       #休眠间隔,防系统卡
            self.signal_ID.emit(self.testN)   #将本线程中的testN值(0-99)通过信号signal_ID发送出去,窗口接收端通过绑定信号signal_ID的槽函数可及时接收于此数据
        
    #停止退出线程
    def stop(self):
        print(f'停止线程{self.ID}')
        self.terminate()

    #得到本线程的ID
    def getID(self):
        return self.ID
     
#自定义继承calcuteThread多线程子类01,此子类对应完成某一种类型的计算功能:
class trdCalcute_01(calcuteThread):
    def __init__(self,ID, lstDatas=None,sleepTime=0.01,parent=None):
        super().__init__(ID,lstDatas,sleepTime,parent)   #执行多线程基类构造
        self.info=f'本线程ID号={ID},用于计算第一种功能'     
        self.test=0.0
    #重载run函数:如不显示调用其父类的run,则父类的run()方法将不会被调用
    def run(self):
        print(f'trdCalcute_01多线程子类开始run{self.ID}')
        #super().run()              #调用父类calcuteThread的run方法,这里不用
        self.test_calcute1()         #执行计算1
        self.workPersent=100                     #多线程计算作任务完成的百分比值
        self.signal_OK.emit(self.workPersent)    #将本线程中的完成计算任务的百分比发送回调用处,当达到100时,调用处进则关闭此线程和进行下一步

    #多线程数据计算函数1示例
    def test_calcute1(self):
        if(self.lstDatas==None):
            return
        if(len(self.lstDatas)==0):
            return 
        n=10
        loopNum=3000000
        if(self.ID==3):
            n=10          #测试:3线程对list列表的0-5元素*10
        elif(self.ID==4):
            n=20          #测试:4线程对np数组的5-9元素*20
            loopNum=5000000
        lst1=self.lstDatas[0]    #多线程中要处理的数据集合示例,ID=3线程对应的是list列表,ID=4线程为np一维数组结构
        print(lst1)
        startPos=self.lstDatas[1] #本多线程实例要处理的数据区间起点号
        endPos=self.lstDatas[2]   #本多线程实例要处理的数据区间终点号
        
        for index in range(startPos,endPos):
            lst1[index]=lst1[index]*n           #直接对列表成员操作,因列表为内存共享,调用处的列表(或字典,np等)变量在多线程中就处理完成,不必在主进程中再处理此列表变量了,注意避免不同线程或进程对同一区域的数据同时处理出现不可预知的错误(要同时也行,用线程锁,将会降低效率)
            for i in range(loopNum):  #模拟复杂计算:循环300万和500次次的一个计算测试
                self.test+=sqrt(sqrt(sqrt(sqrt(i**1.23456789**0.975312468)*sin(i**0.123456789/pi)*sqrt(i**0.0123456789**0.13579**0.2468/pi))))
        self.workOK=True                        #本线程要计算的任务已全部完成
          
      #如必要,从线程返回计算结果.如传入线程的数据集是列表,字典等数据,因是内存共享,在线程计算完成后,共享数据部分在调用处已无需再处理,非内存共享数据从此函数中返回得到        
    def getCalcuteValue(self):
        return self.test         

#自定义继承calcuteThread多线程子类02,此子类对应完成xxxx种类型的计算功能:
class trdCalcute_02(calcuteThread):
    def __init__(self,ID, lstDatas=None,sleepTime=0.01,parent=None):
        super().__init__(ID,lstDatas,sleepTime,parent)   #执行多线程基类构造
        self.info=f'本线程ID号={ID},用于计算第二种计算功能'     

    #重载run函数:如不显示调用其父类的run,则父类的run()方法将不会被调用
    def run(self):
        print(f'trdCalcute_02多线程子类开始run{self.ID}')
        #super().run()              #调用父类calcuteThread的run方法,这里不用
        self.calOrgToEndZ()         #执行计算
        self.workPersent=100                     #多线程计算作任务完成的百分比值
        self.signal_OK.emit(self.workPersent)    #将本线程中的完成计算任务的百分比发送回调用处,当达到100时,调用处进则关闭此线程和进行下一步
 
    #多线程数据计算函数2示例
    def test_calcute2(self):
        if(self.lstDatas==None):
            return
        if(len(self.lstDatas)==0):
            return 
        #计算测试
        for i in range(50000000):  #循环50000000的一个计算测试
            self.test+=sqrt(sqrt(sqrt(sqrt(i**1.23456789**0.975312468)*sin(i**0.123456789/pi)*sqrt(i**0.0123456789**0.13579**0.2468/pi))))
        self.workOK=True  #本线程要计算的任务已全部完成
  
    #如必要,从线程返回计算结果.如传入线程的数据集是列表,字典等数据,因是内存共享,在线程计算完成后,共享数据部分在调用处已无需再处理,非内存共享数据从此函数中返回得到        
    def getCalcuteValue(self):
        return self.test
    
#自定义异常类,在使用代码处如认为出现异常,用代码raise customExcept('自定义异常,ID=1',1) ,异常处thy:.... except customExcept as e: print(e.msg)
class customExcept(Exception):
    def __init__(self,msg,ID=0):
        self.msg=msg
        if(ID==1):
            self.msg='自定义异常1'
        elif(ID==2):
            self.msg='自定义异常2'
        
#########################################################################################
if __name__=="__main__": 
    app = QApplication([])
    mainWin = MainWindow()
    mainWin.show()
    app.exec()

2、示例2,在采用多线程时,如果对同一数据有多个线程在某个时间点同时要处理,就可能存在数据处理的结果不是预想中的了(上例是分段各处理各的,不存在此问题),就需要考虑用线程锁,即对某一数据处理前先加上锁表示处理过程中不准其他线程再使用此数据,处理完成后解锁此数据,其他线程就可以使用此数据了。

"""
1示例:同时开启的多线程因线程处理时序不同,对共有全局变量的引用值可能是同预期值是不同的
2示例:使用线程锁,来避免1示例可能出现的情况,但会降低线程使用效率
"""
import threading
import time
from timeit import default_timer as timer
import time
from time import *

import sys
statrtime=0.00 #线程起止时间
endtime=0.00
Lock = threading.Lock()  
# myThread继承父类(无锁示例),并进行重写
class myThread1(threading.Thread):
    # 重写父类的构造函数
    count=0
    lst=['类全局成员列表值=',0,'列表中的循环计数=',0]
    def __init__(self, number, index):
        threading.Thread.__init__(self)
        self.number = number  # 添加number变量
        self.id = index  # 添加letter变量
        
    # 重写父类中的run函数
    def run(self):
        if(self.id==1):
            print(f"【线程开始】{self.name}")
            self.task1(self.number, self.id)
        elif(self.id==2):
            print(f"【线程开始】{self.name}")
            self.task2(self.number, self.id)
       # print("【线程结束】", self.name)
 
    # 重写父类析构函数
    def __del__(self):
        print("【线程销毁释放内存】", self.name)
 
 
    # 自定义的函数:供线程1调用
    def task1(self, number, id):
        count=1
        i=1
        while i <= number:    
           #sleep(0.1)
            myThread1.lst[1]=i    
            myThread1.lst[3]=count  
            myThread1.count+=1
            num=myThread1.count
            s=''
            sleep(0.04)          #因休眠期,可能存在其他跑得快的线程将此全局变量改了,即myThread.count不一定是连续的
            if(myThread1.count!=num):
                s='两值已不相同'
            print(f'\ntask1:线程{id}中lst={myThread1.lst},线程类全局计数count={myThread1.count},对比sleep前值={num}:{s}')
            i+=1  
            i+=1
            count+=1

    # 自定义的函数,供线程2调用
    def task2(self, number, id):
        count=1
        i=1
        m = 1
        while i <= number:    
            myThread1.lst[1]=i    
            myThread1.lst[3]=count  
            myThread1.count+=1
            num=myThread1.count
            s=''
            sleep(0.01)          #因休眠期,可能存在其他跑得快的线程将此全局变量改了,即myThread.count不一定是连续的
            if(myThread1.count!=num):
                s='两值已不相同'
            print(f'\ntask2:线程{id}中lst={myThread1.lst},线程类全局计数count={myThread1.count},对比sleep前值={num}:{s}')
            i+=1
            count+=1

 
# myThread2继承父类(有锁示例),并进行重写
class myThread2(threading.Thread):
    # 重写父类的构造函数
    count=0
    lst=['类全局成员列表值=',0,'列表中的循环计数=',0]
    def __init__(self, number, index):
        threading.Thread.__init__(self)
        self.number = number  # 添加number变量
        self.id = index  # 添加letter变量
          
    # 重写父类中的run函数
    def run(self):
        if(self.id==1):
            print(f"【线程开始】{self.name}")
            self.task3(self.number, self.id)
        elif(self.id==2):
            print(f"【线程开始】{self.name}")
            self.task4(self.number, self.id)
       # print("【线程结束】", self.name)
 
    # 重写父类析构函数
    def __del__(self):
        print("【线程销毁释放内存】", self.name)
 
     # 自定义的函数:供线程2调用
    def task3(self, number, id):
        count=1
        i=1
        while i <= number:    
           #sleep(0.1)
            Lock.acquire()  # 设置线程锁
            myThread2.lst[1]=i    
            myThread2.lst[3]=count  
            myThread2.count+=1
            num=myThread2.count
            s=''
            sleep(0.04)          #因休眠期,可能存在其他跑得快的线程将此全局变量改了,即myThread.count不一定是连续的
            if(myThread2.count!=num):
                s='两值已不相同'
            print(f'\ntask3:线程{id}中lst={myThread2.lst},线程类全局计数count={myThread2.count},对比sleep前值={num}:{s}')
            Lock.release()  # 释放线程锁
            i+=1  
            i+=1
            count+=1

    # 自定义的函数,供线程2调用
    def task4(self, number, id):
        count=1
        i=1
        m = 1
        while i <= number:    
            Lock.acquire()  # 设置线程锁
            myThread2.lst[1]=i    
            myThread2.lst[3]=count  
            myThread2.count+=1
            num=myThread2.count
            s=''
            sleep(0.01)          #因休眠期,可能存在其他跑得快的线程将此全局变量改了,即myThread.count不一定是连续的
            if(myThread2.count!=num):
                s='两值已不相同'
            print(f'\ntask4:线程{id}中lst={myThread2.lst},线程类全局计数count={myThread2.count},对比sleep前值={num}:{s}')
            Lock.release()  # 释放线程锁
            i+=1
            count+=1


if __name__ == '__main__':
    print('开始示例1:开启两个线程,不锁线程......')
    starttime = timer()
    thread1 = myThread1(80, 1)  # 创建线程thread1:
    thread2 = myThread1(150, 2)  # 创建线程thread2:
    thread1.start()  # 启动线程1
    thread2.start()  # 启动线程2
    thread1.join()  # 等待线程1
    thread2.join()  # 等待线程2
    endtime = timer()
    sumtime=(endtime-starttime)*1000
    print(f'示例1运行总时间{sumtime}毫秒\n\n5秒后准备开始运行示列2:有锁线程.....')
    sleep(5)
    
    print('开始示例2:开启两个线程,锁线程......')
    starttime = timer()
    thread3 = myThread2(80, 1)   # 创建线程thread3:
    thread4 = myThread2(150, 2)  # 创建线程thread4:
    thread3.start()  # 启动线程3
    thread4.start()  # 启动线程4
    
    thread3.join()  # 等待线程3
    thread4.join()  # 等待线程4
    endtime = timer()
    sumtime=(endtime-starttime)*1000
    print(f'示例2运行总时间{sumtime}毫秒')

3、第三种使用多线程的方式是,先建立一管理多线程,此多线程负责管理其他新建的工作用多线程,管理线程中是靠一字典变量来保存和工作线程对象,接着再建立若干工作多线程,用这些线程来完成计算等工作任务,在管理线程中来打开或关闭工作线程。

示例代码运行界面

#模块功能:基于Python的多线程类的示例:演示多线程管理类trdManager和多线程类trdWork及其子类trdWork_03....等来执行多线程数据计算,
#         用于将大量复杂的数据计算包装到多线程类中来完成
#         线程1和线程2演示多线程同主窗口数据通信,线程3演示用继承多线程子类trdWork_03执行另一种计算任务
#         一个线程类可初始化多个对象,对相同计算功能的可用一个同名线程类定义多个实例化线程对象,
#         每个线程对象执行相同的计算功能完成对拆分的大数据分块计算,不同计算功能的可分别继承定义不同功能的线程子类并实例化对象
import sys,time,random,math
from PySide6 import *
from PySide6.QtWidgets import *
from PySide6.QtCore import *
#from PySide6.QtGui import *      #如果运行时没有任何界面调出,也不报错,请屏蔽此行,原因不详
import PySide6.QtCharts

from PySide6.QtCore import Signal, QEvent,Property, QSize
from PySide6.QtCore import (QDateTime, QFile,QDir, QLibraryInfo, QSysInfo, Qt,
                            QTimer,Slot,  QAbstractTableModel, QModelIndex,
                            QPoint,QPointF,QStandardPaths, QUrl, QIODevice, QRectF,qFatal,qWarning,qVersion)
from PySide6.QtGui import (QCursor,QIcon,QImage,QPicture,QDesktopServices, QGuiApplication,
                           QKeySequence, QShortcut, QStandardItem,QStandardItemModel)
from PySide6.QtGui import (QPen,QBrush,QColor,QFont, QPainter,QGradient,QMatrix4x4,
                            QPlatformSurfaceEvent, QSurface, QWindow,QSurfaceFormat)
from PySide6.QtGui import (QRhi, QRhiBuffer,QPixmap,QAction,QWheelEvent,
                           QRhiDepthStencilClearValue,
                           QRhiGraphicsPipeline, QRhiNullInitParams,
                           QRhiGles2InitParams, QRhiRenderBuffer,
                           QRhiSampler, QRhiShaderResourceBinding,
                           QRhiShaderStage, QRhiTexture,QMovie,
                           QRhiVertexInputAttribute, QRhiVertexInputBinding,
                           QRhiVertexInputLayout, QRhiViewport, QShader)
from PySide6.QtWidgets import (QApplication, QDialog,QWidget, QFileDialog, QMainWindow, QMessageBox)
from PySide6.QtWidgets import (QCheckBox, QComboBox,
                               QCommandLinkButton, QDateTimeEdit, QDial,
                               QDialog, QDialogButtonBox, QFileSystemModel,
                               QGridLayout, QGroupBox, QHBoxLayout, QLabel,
                               QLineEdit, QListView, QMenu, QPlainTextEdit,
                               QProgressBar, QPushButton, QRadioButton,
                               QScrollBar, QSizePolicy, QSlider, QSpinBox,
                               QStyleFactory, QTableWidget, QTabWidget,
                               QTextBrowser, QTextEdit, QToolBox, QToolButton,
                               QTreeView, QVBoxLayout)


import threading  #Py的多线程类
from math import *


#基于QT的演示窗口类
class MainWindow(QWidget):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        global bTrd00, bTrd01
        self.setWindowTitle('多线程(同时开4个)示例')
        self.resize(900,500)
        self.startTime5=self.endTime5=0.0  
        self.endTime6=self.endTime6=0.0 
        layout = QFormLayout()   
        self.pushButton_1 = QPushButton()
        self.pushButton_1.setText("开始新线程")
        self.pushButton_2 = QPushButton()
        self.pushButton_2.setText("开始新线程2")
        self.pushButton_3 = QPushButton()
        self.pushButton_3.setText("开始新线程3")
        self.pushButton_4 = QPushButton()
        self.pushButton_4.setText("停止线程1")
        self.pushButton_5 = QPushButton()
        self.pushButton_5.setText("停止线程2")
        self.pushButton_6 = QPushButton()
        self.pushButton_6.setText("停止线程3")

        self.edita1 = QLineEdit()
        self.edita1.setText('信号槽:线程1+2的值')
        self.progressBar_1=QProgressBar()
        self.progressBar_1.setMinimum(0)
        self.progressBar_1.setMaximum(100)
        self.edita2 = QLineEdit()
        self.edita2.setText('信号槽:线程3+4的值')
        self.progressBar_2=QProgressBar()
        self.progressBar_2.setMinimum(0)
        self.progressBar_2.setMaximum(100)
        self.edita3 = QLineEdit()
        self.edita3.setText('信号槽:线程5的值')

        layout.addRow("单击按纽开始线程1(线程sleep 500毫秒)->",self.pushButton_1)
        layout.addRow("单击按纽结束线程1->",self.pushButton_4)
        layout.addRow("计时器不间断查询返回线程1的值->",self.edita1)
        layout.addRow("线程1返回值(进度条展示)->",self.progressBar_1)
        layout.addRow("单击按纽开始线程2(线程sleep 20毫秒)->",self.pushButton_2)
        layout.addRow("单击按纽结束线程2->",self.pushButton_5)
        layout.addRow("计时器不间断查询返回线程2的值->",self.edita2)
        layout.addRow("线程2返回值(进度条展示)->",self.progressBar_2)
        layout.addRow("单击按纽开始线程3->",self.pushButton_3)
        layout.addRow("单击按纽结束线程3->",self.pushButton_6)
        layout.addRow("计时器不间断查询得到的线程3计算结果->",self.edita3)
       
        self.setLayout(layout)
        self.pushButton_4.setEnabled(False)
        self.pushButton_5.setEnabled(False)
        self.pushButton_6.setEnabled(False)

        #定义线程管理器
        self.trd_manages=trdManager()
        self.trdWork_1=trdWork(1,'线程1',0.5) 
        self.trdWork_2=trdWork(2,'线程2',0.02)
        self.trdWork_3=trdWork_03(3,'线程3',0.001)     #继承自trdWork的另一不同的run()计算代码类
        self.pushButton_1.clicked.connect(self.start_worker_1)
        self.pushButton_2.clicked.connect(self.start_worker_2)
        self.pushButton_3.clicked.connect(self.start_worker_3)
        self.pushButton_4.clicked.connect(self.stop_worker_1)
        self.pushButton_5.clicked.connect(self.stop_worker_2)
        self.pushButton_6.clicked.connect(self.stop_worker_3)
        
        # 初始化一个QT定时器1,对线程1和线程2实时查询数据计算结果
        self.timerID1 = QTimer(self)
        self.timerID1.start(20)
        # 将定时器与槽函数qtTimeID1连接
        self.timerID1.timeout.connect(self.qtTimeID1)  

        # 初始化一个QT定时器2,对线程3实时查询数据计算结果
        self.timerID3 = QTimer(self)
        self.qtStartTimerID3(0.1)
        # 将定时器与槽函数qtTimeID3连接
        self.timerID3.timeout.connect(self.qtTimeID3)  

    #QT计时器函数体: 每隔指定时间查询多线程中的计算完成情况
    def qtTimeID1(self):
        if(self.trdWork_1.is_running):
            count1=self.trdWork_1.testCount
            count2=self.trdWork_2.testCount
            self.edita1.setText(f'工作线程self.trdWork_1中的值为={count1}')
            self.edita2.setText(f'工作线程self.trdWork_2中的值为={count2}')
            self.progressBar_1.setValue(count1)
            self.progressBar_2.setValue(count2)
    def qtEndTimerID1(self):
        if(self.timerID1.isActive()):
            self.timerID1.stop()

    #开始QT类计时器,计时器间隔时间1000毫秒
    def qtStartTimerID3(self,timeSleep):
        if(self.timerID3.isActive()):
            self.timerID3.stop()
        self.timerID3.start(timeSleep*20)

    #QT计时器函数体: 每隔指定时间查询多线程中的计算完成情况
    def qtTimeID3(self):
        if(self.trdWork_3.is_running and (not self.trdWork_3.is_workOK)):
            self.edita3.setText(f'工作线程self.trdWork_3中当前正在计算,当前计算值={self.trdWork_3.testCount}')
        elif(self.trdWork_3.is_workOK):
            self.edita3.setText(f'工作线程self.trdWork_3已完成全部计算,最终计算结果={self.trdWork_3.testCount}')

    def qtEndTimerID3(self):
        if(self.timerID3.isActive()):
            self.timerID3.stop()

                       
    #点击按纽1的信号槽:开始线程1
    def start_worker_1(self):
        self.pushButton_1.setEnabled(False)
        self.pushButton_4.setEnabled(True)
        self.trd_manages.start_thread(self.trdWork_1)  #开始工作线程1
        self.trdWork_1.is_running=True

    #点击按纽2的信号槽:开始线程3+接收线程4
    def start_worker_2(self):
        self.pushButton_2.setEnabled(False)
        self.pushButton_5.setEnabled(True)
        self.trd_manages.start_thread(self.trdWork_2)  #开始工作线程2 

    #点击按纽3的信号槽:开始线程3,用于计算
    def start_worker_3(self):
        self.pushButton_3.setEnabled(False)
        self.pushButton_6.setEnabled(True)
        self.trd_manages.start_thread(self.trdWork_3)  #开始工作线程3
        self.startTime3=time.time()  #记录线程3从开始到计算完成用的时间
        self.edita3.setText('开始循环20000000次计算一复杂计算式,请等待。。。')
        
    #点击按纽4的信号槽:结束线程1
    def stop_worker_1(self):
        self.pushButton_1.setEnabled(True)
        self.pushButton_4.setEnabled(False)
        #self.trd_manages.stop_threadID(1)
        self.trd_manages.stop_thread(self.trdWork_1)
        
    #点击按纽5的信号槽:结束线程2
    def stop_worker_2(self):
        self.pushButton_2.setEnabled(True)
        self.pushButton_5.setEnabled(False)
        self.trd_manages.stop_thread(self.trdWork_2)    

    #点击按纽6的信号槽:结束线程5
    def stop_worker_3(self):
        self.pushButton_3.setEnabled(True)
        self.pushButton_6.setEnabled(False)
        self.trd_manages.stop_thread(self.trdWork_3)  
        

    #线程中直接调用主窗口句柄对象对应的方法:此方法不建议采用,可能频繁调用会造成运行错误 
    def threadToWindow(self,putID,reValue):
        pass
#*************************************************************************************************
#定义工作线程类
class trdWork():
    def __init__(self,ID,description,timeSleep=0.5):
        self.description=description   #线程名称
        self.ID=ID                     #线程ID号,同列表下标一致 
        self.testCount=0               #测试用自增1变量        
        print(description)
        self.is_workOK=False       #本线程工作是否全部完成
        self.is_running=True       #控制线程的循环体,为False时会退出循环体到run函数体尾部,表示本线程工作完成
        self.timeSleep=timeSleep   #为防系统卡顿,设置循环体中间隔休眠时间
        
    #线程工作函数,本函数执行到最后一行代码视作本线程工作完成
    def run(self):
        while self.is_running:
            self.testCount+=1
            if(self.testCount>100):self.testCount=0
            #print(f'工作线程ID={self.ID},run函数循环体内执行计数={self.testCount}\n')
            time.sleep(self.timeSleep)

        self.is_workOK=True #本工作线程已全部完成计算工作
        print(f'线程ID={self.ID}run函数体工作已全部完成')

    #停止线程
    def stop(self,bRust=True):
        if(not self.is_workOK):
            print(f'线程还未完成任务,如强行停止,可能会造成计算错误')
            if(bRust):
                self.is_running=False
        else:
            if(bRust):
                self.is_running=False

#定义工作线程类
class trdWork_03(trdWork):
    def __init__(self,ID,description,timeSleep=0.5):
        super().__init__(ID,description,timeSleep)

    #重载run,以实现另一种计算过程,重载后父类中的run不会再被调用
    def run(self):
        loopNum=20000000
        for i in range(loopNum):
            if(self.is_running):
                self.testCount+=sqrt(sqrt(sqrt(sqrt(i**1.23456789**0.975312468**0.05642317895412)*sin(i**0.123456789/pi)*sqrt(i**0.0123456789**0.13579**0.2468/pi))))
            else:
                print(f'线程{self.ID}被强行停止,但当前的计算工作还没有完成')
                self.is_workOK=True
                return 
        self.is_workOK=True #本工作线程已全部完成计算工作
        print(f'线程ID= {self.ID} run函数体工作已全部完成')
  
#自定义计算用多线程管理类(每start_thread方法一次就会增加一个线程)
class trdManager():
    def __init__(self):
        self.dicthreads={}  #以字典方式记录各线程,其中字典key=ID
    #开始一个线程(本类每调用一次增加一个线程,可同ID号,会覆盖此ID线程)
    def start_thread(self,task):
        newthread=threading.Thread(target=task.run)
        ID=task.ID
        if(self.dicthreads.get(ID,None)!=None):
            print(f'多线程ID={ID}曾经被创建过或正在运行中。。。')
            raise customExcept(0)   #抛出异常
        self.dicthreads[ID]=(newthread,task)
        print(f'\n************新线程的self.ID={ID}************')
        newthread.start()
    #关闭指定ID号的线程    

    def stop_threadID(self,ID): 
            trds=self.dicthreads.get(ID,None)
            curThread=trds[0]
            task=trds[1]
            if(curThread!=None):
                curThread.stop()
                task.join()
                print(f'\n线程ID={curThread.ID}被停止')
    #关闭指定的线程    
    def stop_thread(self,thread): 
            for key in self.dicthreads:
                curThread=self.dicthreads[key][0]
                dask=self.dicthreads[key][1]
                if(thread==dask):
                    thread.stop()
                    dask.join()
                    print(f'\n线程ID={curThread.ID}被停止')


    #得到指定名称的线程的ID
    def getID(self,task):
        for key in self.dicthreads.values:
            pass

#自定义异常类,在使用代码处如认为出现异常,用代码raise customExcept('自定义异常,ID=1',1) ,异常处thy:.... except customExcept as e: print(e.msg)
class customExcept(Exception):
    def __init__(self,msg,ID=0):
        self.msg=msg
        if(ID==0):
            self.msg='自定义异常1:多线程ID曾经被创建过或正在运行中。。。,新线程ID可能会同正在运行的线程ID号冲突'
        elif(ID==1):
            self.msg='自定义异常2'
        
#########################################################################################
    app = QApplication([])
    mainWin = MainWindow()
    mainWin.show()
    app.exec()

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2303232.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

神经网络八股(1)

1.什么是有监督学习&#xff0c;无监督学习 有监督学习是带有标签的&#xff0c;无监督学习是没有标签的&#xff0c;简单来说就是有监督学习的输入输出都是固定的&#xff0c;已知的&#xff0c;无监督学习输入是已知的&#xff0c;输出是不固定的&#xff0c;无监督学习是通…

深度学习每周学习总结Y1(Yolov5 调用官方权重进行检测 )

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客Y1中的内容 &#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 ** 注意该训练营出现故意不退押金&#xff0c;恶意揣测偷懒用假的结果冒充真实打卡记录&#xff0c;在提出能够拿到视频录像…

计算机视觉基础|从 OpenCV 到频域分析

一、引言 在当今数字化时代&#xff0c;图像处理已渗透到我们生活的方方面面&#xff0c;从日常使用的智能手机拍照美化&#xff0c;到医学领域的精准诊断&#xff0c;再到自动驾驶中的环境感知&#xff0c;其重要性不言而喻。在图像处理领域中&#xff0c;OpenCV 和频域分析&…

74. 搜索二维矩阵(LeetCode 热题 100)

题目来源; 74. 搜索二维矩阵 - 力扣&#xff08;LeetCode&#xff09; 题目内容&#xff1a; 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。 每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &am…

netcore libreoffice word转pdf中文乱码

一、效果 解决&#xff1a; cd /usr/share/fonts/ mkdir zhFont cd zhFont #windows系统C:\Windows\Fonts 中复制/usr/share/fonts/zhFont sudo apt update sudo apt install xfonts-utils mkfontscale mkfontdir #刷新字体缓存 fc-cache -fv #查看已安装的字体列表 fc-list :…

qt-C++笔记之创建和初始化 `QGraphicsScene` 和 `QGraphicsView` 并关联视图和场景的方法

qt-C笔记之创建和初始化 QGraphicsScene 和 QGraphicsView 并关联视图和场景的方法 code review! 参考笔记 1.qt-C笔记之创建和初始化 QGraphicsScene 和 QGraphicsView 并关联视图和场景的方法 2.qt-C笔记之QGraphicsScene和 QGraphicsView中setScene、通过scene得到view、通过…

OpenGL 01--构建GLFW、创建第一个工程、配置GLAD

一、OpenGL介绍 一般它被认为是一个API(Application Programming Interface, 应用程序编程接口)&#xff0c;包含了一系列可以操作图形、图像的函数。然而&#xff0c;OpenGL本身并不是一个API&#xff0c;它仅仅是一个由Khronos组织制定并维护的规范(Specification)。 OpenGL规…

【时时三省】(C语言基础)求多项式1-1/2+1/3-1/4+...+1/99-1/100的值 用C语言表示

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 示例&#xff1a; 输出结果是 0.688172

kafka-集群缩容

一. 简述&#xff1a; 当业务增加时&#xff0c;服务瓶颈&#xff0c;我们需要进行扩容。当业务量下降时&#xff0c;为成本考虑。自然也会涉及到缩容。假设集群有 15 台机器&#xff0c;预计缩到 10 台机器&#xff0c;那么需要做 5 次缩容操作&#xff0c;每次将一个节点下线…

Ubuntu22.04 - etcd的安装和使用

目录 介绍安装Etcd安装etcd的客户端使用 介绍 Etcd 是一个 golang 编写的分布式、高可用的一致性键值存储系统&#xff0c;用于配置共享和服务发现等。它使用 Raft 一致性算法来保持集群数据的一致性&#xff0c;且客户端通过长连接watch 功能&#xff0c;能够及时收到数据变化…

排查JVM的一些命令

查看JVM相关信息的方法 环境&#xff1a; Win10, jdk17 查看端口的Pid netstat -ano | findstr <端口号>列出当前运行的JVM进程 ## 用于输出JVM中运行的进程状态信息。通过jps&#xff0c;可以快速获取Java进程的PID&#xff08;进程标识符&#xff09;&#xff0c; …

Arduino 第十六章:pir红外人体传感器练习

Arduino 第十六章&#xff1a;PIR 传感器练习 一、引言 在 Arduino 的众多有趣项目中&#xff0c;传感器的应用是非常重要的一部分。今天我们要学习的主角是 PIR&#xff08;被动红外&#xff09;传感器。PIR 传感器能够检测人体发出的红外线&#xff0c;常用于安防系统、自动…

自动化之ansible(二)

一、ansible中playbook&#xff08;剧本&#xff09; 官方文档&#xff1a; Ansible playbooks — Ansible Community Documentation 1、playbook的基本结构 一个基本的playbook由以下几个主要部分组成 hosts: 定义要执行任务的主机组或主机。 become: 是否需要使用超级用户…

QSNCTF-WEB做题记录

第一题&#xff0c;文章管理系统 来自 <天狩CTF竞赛平台> 描述&#xff1a;这是我们的文章管理系统&#xff0c;快来看看有什么漏洞可以拿到FLAG吧&#xff1f;注意&#xff1a;可能有个假FLAG哦 1&#xff0c;首先观察题目网站的结构和特征 这个一个文件管理系统&#x…

Ruoyi-Vue 3.8.7集成积木报表JmReport和积木大屏JimuBI

Ruoyi-Vue 3.8.7集成积木报表JmReport和积木大屏JimuBI 一、版本 RuoYi-Vue版本&#xff1a;v3.8.7 JMreport报表版本&#xff1a; v1.9.4 JimuBI大屏版本&#xff1a;V1.9.4 二、数据库 积木数据库sql 下载后&#xff0c;使用数据库管理工具执行sql脚本&#xff0c;将需…

亲测可用,IDEA中使用满血版DeepSeek R1!支持深度思考!免费!免配置!

作者&#xff1a;程序员 Hollis 之前介绍过在IDEA中使用DeepSeek的方案&#xff0c;但是很多人表示还是用的不够爽&#xff0c;比如用CodeChat的方案&#xff0c;只支持V3版本&#xff0c;不支持带推理的R1。想要配置R1的话有特别的麻烦。 那么&#xff0c;今天&#xff0c;给…

jvm中各个参数的理解

MEMORY - MANAGERS 定义 MEMORY - MANAGERS即内存管理器&#xff0c;它是操作系统或软件系统中负责管理计算机内存资源的组件。从本质上来说&#xff0c;它是一种软件机制&#xff0c;旨在协调计算机系统中内存的分配、使用和回收等操作&#xff0c;确保系统能够高效、稳定地…

【队列】循环队列(Circular Queue)详解

文章目录 一、循环队列简介二、循环队列的判空和判满三、循环队列的实现leetcode 622. 设计循环队列 一、循环队列简介 在实际开发中&#xff0c;队列是一种常用的数据结构&#xff0c;而循环队列&#xff08;Circular Queue&#xff09;则一般是一种基于数组实现的队列&#x…

DeepSeek掀起推理服务器新风暴,AI应用迎来变革转折点?

AI 浪潮下&#xff0c;推理服务器崭露头角 在科技飞速发展的当下&#xff0c;AI 是耀眼明星&#xff0c;席卷各行业&#xff0c;深刻改变生活与工作模式&#xff0c;从语音助手到医疗诊断、金融风险预测&#xff0c;AI 无处不在。其发展分数据收集整理、模型训练、推理应用三个…

Vue 项目中逐步引入 TypeScript 的类型检查

在现有的 Vue 项目中逐步引入 TypeScript 的类型检查 本文源于一道面试题&#xff1a;注&#xff1a;两种问法一个意思哈&#xff01;&#xff01; 问题一&#xff1a;“ 老项目Js写的&#xff0c;如何轻量方式享受 ts 类型&#xff1f;” 问题二&#xff1a;“如何 在现有的 …