PyQt python 异步任务,多线程,进阶版

初级版见:

https://blog.csdn.net/q610098308/article/details/145175625

PyQt 多线程编程模式:通用的多线程执行器

方案一:装饰器方式(最简洁)

复制代码
import sys
import time
from functools import wraps
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QTextEdit, QVBoxLayout, QWidget, QProgressBar


class AsyncExecutor(QObject):
    """通用的异步执行器"""
    
    started = pyqtSignal()
    finished = pyqtSignal(object)
    progress = pyqtSignal(int)
    error = pyqtSignal(str)
    
    def __init__(self):
        super().__init__()
        self.thread = QThread()
        self.moveToThread(self.thread)
        self.thread.start()
    
    @pyqtSlot()
    def execute(self, func, args=(), kwargs={}):
        """在子线程中执行函数"""
        try:
            self.started.emit()
            result = func(*args, **kwargs)
            self.finished.emit(result)
        except Exception as e:
            self.error.emit(str(e))


def async_execute(on_started=None, on_finished=None, on_error=None, on_progress=None):
    """装饰器:让任何函数都能在子线程中异步执行"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            # 创建执行器
            executor = AsyncExecutor()
            
            # 连接信号
            if on_started:
                executor.started.connect(on_started)
            if on_finished:
                executor.finished.connect(on_finished)
            if on_error:
                executor.error.connect(on_error)
            if on_progress:
                executor.progress.connect(on_progress)
            
            # 异步执行
            executor.execute(func, args, kwargs)
            return executor
            
        return wrapper
    return decorator


# 使用示例
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.init_ui()
        
    def init_ui(self):
        self.setWindowTitle("简化版多线程执行器")
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        layout = QVBoxLayout(central_widget)
        
        self.btn1 = QPushButton("执行耗时计算")
        self.btn2 = QPushButton("执行文件处理") 
        self.btn3 = QPushButton("执行网络请求")
        self.text = QTextEdit()
        
        layout.addWidget(self.btn1)
        layout.addWidget(self.btn2)
        layout.addWidget(self.btn3)
        layout.addWidget(self.text)
        
        # 连接按钮 - 使用装饰器方式
        self.btn1.clicked.connect(self.on_calculation_task)
        self.btn2.clicked.connect(self.on_file_task)
        self.btn3.clicked.connect(self.on_network_task)
    
    def log(self, msg):
        self.text.append(f"[{time.strftime('%H:%M:%S')}] {msg}")
    
    # 方法1:直接使用装饰器
    @async_execute(
        on_started=lambda self: self.log("计算任务开始..."),
        on_finished=lambda self, result: self.log(f"计算结果: {result}"),
        on_error=lambda self, error: self.log(f"计算错误: {error}")
    )
    def heavy_calculation(self, n=1000000):
        """耗时计算任务"""
        result = 0
        for i in range(n):
            result += i * i
            if i % 100000 == 0:  # 模拟进度更新
                pass
        return f"计算完成,结果: {result}"
    
    def on_calculation_task(self):
        self.heavy_calculation(500000)
    
    # 方法2:动态连接方式
    def on_file_task(self):
        @async_execute(
            on_started=lambda: self.log("文件处理开始..."),
            on_finished=lambda result: self.log(f"文件处理完成: {result}")
        )
        def process_files():
            """模拟文件处理"""
            time.sleep(2)
            return "处理了3个文件"
        
        process_files()
    
    # 方法3:更灵活的手动连接方式
    def on_network_task(self):
        def network_operation():
            """模拟网络请求"""
            time.sleep(3)
            return "获取到10条数据"
        
        executor = AsyncExecutor()
        executor.started.connect(lambda: self.log("网络请求开始..."))
        executor.finished.connect(lambda result: self.log(f"网络请求完成: {result}"))
        executor.execute(network_operation)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

方案二:链式调用方式(更直观)

复制代码
from PyQt5.QtCore import QObject, QThread, pyqtSignal


class EasyAsyncTask:
    """链式调用的异步任务执行器"""
    
    def __init__(self):
        self.executor = AsyncExecutor()
        self._func = None
        self._args = ()
        self._kwargs = {}
    
    def run(self, func, *args, **kwargs):
        """设置要执行的函数"""
        self._func = func
        self._args = args
        self._kwargs = kwargs
        return self
    
    def on_start(self, callback):
        """任务开始回调"""
        self.executor.started.connect(callback)
        return self
    
    def on_finish(self, callback):
        """任务完成回调"""
        self.executor.finished.connect(callback)
        return self
    
    def on_error(self, callback):
        """任务错误回调"""
        self.executor.error.connect(callback)
        return self
    
    def start(self):
        """开始执行任务"""
        if self._func:
            self.executor.execute(self._func, self._args, self._kwargs)
        return self


# 使用示例
class EasyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.init_ui()
    
    def init_ui(self):
        self.setWindowTitle("链式调用异步任务")
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        layout = QVBoxLayout(central_widget)
        
        self.btn = QPushButton("执行多个任务")
        self.text = QTextEdit()
        layout.addWidget(self.btn)
        layout.addWidget(self.text)
        
        self.btn.clicked.connect(self.run_multiple_tasks)
    
    def log(self, msg):
        self.text.append(msg)
    
    def run_multiple_tasks(self):
        """同时执行多个不同类型的任务"""
        
        # 任务1:数据处理
        def data_processing():
            time.sleep(2)
            return "数据处理完成"
        
        EasyAsyncTask().run(data_processing)\
            .on_start(lambda: self.log("开始数据处理..."))\
            .on_finish(lambda result: self.log(result))\
            .start()
        
        # 任务2:图片处理
        def image_processing():
            for i in range(5):
                time.sleep(0.5)
            return "图片处理完成"
        
        EasyAsyncTask().run(image_processing)\
            .on_start(lambda: self.log("开始图片处理..."))\
            .on_finish(lambda result: self.log(result))\
            .start()
        
        # 任务3:网络下载
        def download_file():
            time.sleep(3)
            return "文件下载完成"
        
        EasyAsyncTask().run(download_file)\
            .on_start(lambda: self.log("开始下载文件..."))\
            .on_finish(lambda result: self.log(result))\
            .start()

方案三:全局线程池方式(最高效)

复制代码
from PyQt5.QtCore import QThreadPool, QRunnable, pyqtSignal, QObject


class WorkerSignals(QObject):
    """定义工作线程的信号"""
    started = pyqtSignal()
    finished = pyqtSignal(object)
    error = pyqtSignal(str)
    progress = pyqtSignal(int)


class TaskWorker(QRunnable):
    """可重用的任务工作器"""
    
    def __init__(self, func, args=(), kwargs={}):
        super().__init__()
        self.func = func
        self.args = args
        self.kwargs = kwargs
        self.signals = WorkerSignals()
    
    def run(self):
        try:
            self.signals.started.emit()
            result = self.func(*self.args, **self.kwargs)
            self.signals.finished.emit(result)
        except Exception as e:
            self.signals.error.emit(str(e))


class GlobalThreadManager:
    """全局线程管理器"""
    
    _thread_pool = QThreadPool.globalInstance()
    
    @classmethod
    def submit(cls, func, args=(), kwargs={}, 
               on_started=None, on_finished=None, on_error=None):
        """提交任务到全局线程池"""
        worker = TaskWorker(func, args, kwargs)
        
        if on_started:
            worker.signals.started.connect(on_started)
        if on_finished:
            worker.signals.finished.connect(on_finished)
        if on_error:
            worker.signals.error.connect(on_error)
        
        cls._thread_pool.start(worker)
        return worker


# 使用示例 - 超级简洁!
class SimpleWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.init_ui()
    
    def init_ui(self):
        self.setWindowTitle("全局线程池 - 最简单用法")
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        layout = QVBoxLayout(central_widget)
        
        self.btn = QPushButton("一键执行多个任务")
        self.text = QTextEdit()
        layout.addWidget(self.btn)
        layout.addWidget(self.text)
        
        self.btn.clicked.connect(self.simple_async_tasks)
    
    def log(self, msg):
        self.text.append(f"[{time.strftime('%H:%M:%S')}] {msg}")
    
    def simple_async_tasks(self):
        """最简单的异步任务调用"""
        
        # 一行代码执行异步任务!
        GlobalThreadManager.submit(
            lambda: time.sleep(2) or "任务1完成",
            on_started=lambda: self.log("任务1开始"),
            on_finished=lambda result: self.log(result)
        )
        
        GlobalThreadManager.submit(
            lambda: sum(i*i for i in range(1000000)),
            on_started=lambda: self.log("任务2开始"), 
            on_finished=lambda result: self.log(f"任务2结果: {result}")
        )

小结:

方案三(全局线程池)是最推荐的,因为:

  1. 使用最简单:一行代码搞定异步执行

  2. 性能最好:重用线程,避免频繁创建销毁

  3. 资源友好:自动管理线程数量

  4. 功能完整:支持开始、完成、错误回调

    最简单的使用示例

    def my_heavy_function(param1, param2):
    # 你的耗时操作
    time.sleep(2)
    return f"处理结果: {param1} + {param2}"

    异步调用

    GlobalThreadManager.submit(
    my_heavy_function,
    args=("hello", "world"),
    on_started=lambda: print("任务开始"),
    on_finished=lambda result: print(result)
    )

这样封装后,你在任何地方需要异步执行耗时操作时,只需要调用 GlobalThreadManager.submit() 就可以了,非常方便!

相关推荐
一只大袋鼠5 天前
并发编程(三):线程快照统计・grep+awk+sort+uniq 实战详解
java·开发语言·多线程·并发编程
凌云拓界7 天前
TypeWell全攻略(二):热力图渲染引擎,让键盘发光
前端·后端·python·计算机外设·交互·pyqt·数据可视化
_OP_CHEN8 天前
【Linux系统编程】(三十九)吃透线程概念:从底层原理到实战应用
linux·运维·操作系统·线程·进程·多线程·c/c++
凌云拓界9 天前
TypeWell全攻略:AI健康教练+实时热力图开发实战 引言
前端·人工智能·后端·python·交互·pyqt·数据可视化
Dylan的码园9 天前
多线程的创建与管理
java·开发语言·多线程
郝学胜-神的一滴11 天前
单例模式:从经典实现到Vibe Coding时代的思考
开发语言·c++·程序人生·单例模式·设计模式·多线程
天若有情67311 天前
从 try-catch 回调到链式调用:一种更优雅的 async/await 错误处理方案
前端·异常处理·前端开发·async·异步·await·异步编程
郝学胜-神的一滴12 天前
Effective Modern C++ 条款39:一次事件通信的优雅解决方案
开发语言·数据结构·c++·算法·多线程·并发
郝学胜-神的一滴13 天前
Effective Modern C++ 条款38:线程句柄析构行为与Vibe Coding优化指南
开发语言·数据结构·c++·程序人生·多线程·并发
装不满的克莱因瓶13 天前
Java高并发异步请求实战,Jmeter暴力压测下的解决方案
jmeter·线程池·多线程·并发·resttemplate·qps·压测