python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python多线程开发优化

Python多线程应用中的卡死问题优化方案指南

作者:天宁

在利用Python语言开发某查询软件时,遇到了点击搜索按钮后软件卡死的问题,本文将简单分析一下出现的原因以及对应的优化方案,希望对大家有所帮助

问题描述

在开发某查询软件时,遇到了点击搜索按钮后软件卡死的问题。经过分析,主要原因是:

优化方案

1. 网络请求优化

添加超时和重试机制

# app/logic/MmApi.py
import requests
import time
import logging

class MmApi:
    def __init__(self, token):
        self.token = token
        self.timeout = 10  # 设置10秒超时
        self.max_retries = 3  # 最大重试次数
    
    def mm_search(self, params):
        for attempt in range(self.max_retries):
            try:
                response = requests.get(
                    'https://xx.xxx.com/api/search',
                    params=params,
                    headers={'Authorization': f'Bearer {self.token}'},
                    timeout=self.timeout,  # 关键:设置超时
                    verify=False  # 禁用SSL验证
                )
                
                if response.status_code == 200:
                    result = response.json()
                    if result.get('code') == 200:
                        data_list = result.get('data', {}).get('list', [])
                        if not data_list:
                            return {
                                'success': False,
                                'error_type': 'empty_list',
                                'message': '没有找到符合条件的账号'
                            }
                        return {
                            'success': True,
                            'data': data_list,
                            'message': f'成功找到 {len(data_list)} 个账号'
                        }
                    else:
                        # API返回错误
                        return {
                            'success': False,
                            'error_type': 'api_error',
                            'message': f'接口返回错误: {result.get("message", "未知错误")}'
                        }
                else:
                    # HTTP状态码错误
                    return {
                        'success': False,
                        'error_type': 'http_error',
                        'message': f'HTTP请求失败,状态码: {response.status_code}'
                    }
                    
            except requests.exceptions.Timeout:
                logger.warning(f"请求超时,第{attempt + 1}次重试")
                if attempt < self.max_retries - 1:
                    time.sleep(1)  # 重试前等待1秒
                    continue
                return {
                    'success': False,
                    'error_type': 'timeout',
                    'message': '请求超时,请检查网络连接'
                }
                
            except requests.exceptions.ConnectionError:
                logger.warning(f"连接错误,第{attempt + 1}次重试")
                if attempt < self.max_retries - 1:
                    time.sleep(1)
                    continue
                return {
                    'success': False,
                    'error_type': 'connection_error',
                    'message': '网络连接失败,请检查网络设置'
                }
                
            except Exception as e:
                logger.error(f"未知错误: {str(e)}")
                return {
                    'success': False,
                    'error_type': 'unknown_error',
                    'message': f'发生未知错误: {str(e)}'
                }
        
        return {
            'success': False,
            'error_type': 'max_retries_exceeded',
            'message': '超过最大重试次数,请稍后重试'
        }

频率控制优化

# app/logic/YxlmApi.py
import time
from app.Config import API_RATE_LIMIT

class YxlmApi:
    def __init__(self):
        self.enable_rate_limit = API_RATE_LIMIT['enable_rate_limit']
        self.rank_query_delay = API_RATE_LIMIT['rank_query_delay']
        self.hidden_score_delay = API_RATE_LIMIT['hidden_score_delay']
        self.last_rank_request = 0
        self.last_hidden_request = 0
    
    def _wait_for_delay(self, delay_type):
        """等待延迟时间,防止请求过于频繁"""
        if not self.enable_rate_limit:
            return
            
        current_time = time.time()
        if delay_type == 'rank':
            if current_time - self.last_rank_request < self.rank_query_delay:
                wait_time = self.rank_query_delay - (current_time - self.last_rank_request)
                time.sleep(wait_time)
            self.last_rank_request = time.time()
        elif delay_type == 'hidden':
            if current_time - self.last_hidden_request < self.hidden_score_delay:
                wait_time = self.hidden_score_delay - (current_time - self.last_hidden_request)
                time.sleep(wait_time)
            self.last_hidden_request = time.time()
    
    def get_rank(self, puuid):
        self._wait_for_delay('rank')
        # 执行段位查询逻辑
        pass
    
    def get_hidden_score(self, puuid):
        self._wait_for_delay('hidden')
        # 执行隐藏分查询逻辑
        pass

2. 多线程架构优化

使用线程池管理并发任务

# app/controller/Home/BatchSearchController.py
from concurrent.futures import ThreadPoolExecutor
import threading
import queue
import tkinter.messagebox as messagebox

class BatchSearchController:
    def __init__(self, ui):
        self.ui = ui
        self.thread_pool = ThreadPoolExecutor(max_workers=5)  # 限制最大并发数
        self.update_queue = queue.Queue()
        self._start_update_worker()
    
    def _start_update_worker(self):
        """启动UI更新工作线程"""
        def update_worker():
            while True:
                try:
                    func, args, kwargs = self.update_queue.get()
                    if func is None:  # 退出信号
                        break
                    # 在主线程中执行UI更新
                    self.ui.after(0, func, *args, **kwargs)
                except Exception as e:
                    print(f"UI更新异常: {e}")
                finally:
                    self.update_queue.task_done()
        
        update_thread = threading.Thread(target=update_worker, daemon=True)
        update_thread.start()
    
    def safe_ui_update(self, func, *args, **kwargs):
        """安全的UI更新方法"""
        self.update_queue.put((func, args, kwargs))
    
    def search_button(self):
        """搜索按钮点击事件"""
        # 立即禁用搜索按钮,防止重复点击
        self.ui.search_button.config(state='disabled')
        
        # 获取搜索参数
        params = self._get_search_params()
        
        # 在线程池中执行搜索任务
        future = self.thread_pool.submit(self._search_task, params)
        
        # 添加完成回调
        future.add_done_callback(self._on_search_complete)
    
    def _search_task(self, params):
        """搜索任务执行逻辑"""
        try:
            mm = MmApi(self.ui.setting_token.get("1.0", "end").strip())
            mm_result = mm.mm_search(params)
            
            if mm_result and isinstance(mm_result, dict) and 'success' in mm_result:
                if mm_result['success']:
                    # 搜索成功,插入数据
                    self.safe_ui_update(self.insert_data, mm_result['data'])
                else:
                    # 搜索失败,显示错误信息
                    error_type = mm_result.get('error_type', 'unknown')
                    error_message = mm_result.get('message', '未知错误')
                    
                    if error_type == 'empty_list':
                        self.safe_ui_update(messagebox.showwarning, "搜索结果", error_message)
                    else:
                        self.safe_ui_update(messagebox.showerror, "搜索失败", error_message)
            else:
                # 兼容旧格式
                self.safe_ui_update(messagebox.showinfo, "提示", "获取接口出问题,请过几秒再重试")
                
        except Exception as e:
            logger.error(f"搜索任务异常: {str(e)}")
            self.safe_ui_update(messagebox.showerror, "错误", f"搜索过程中发生错误: {str(e)}")
    
    def _on_search_complete(self, future):
        """搜索完成回调"""
        try:
            # 重新启用搜索按钮
            self.safe_ui_update(lambda: self.ui.search_button.config(state='normal'))
        except Exception as e:
            logger.error(f"搜索完成回调异常: {e}")

3. 全局异常处理

设置全局异常处理器

# app/tools/global_var.py
import sys
import traceback
from tkinter import messagebox

def setup_global_exception_handler():
    """设置全局异常处理器"""
    def handle_exception(exc_type, exc_value, exc_traceback):
        # 忽略键盘中断
        if issubclass(exc_type, KeyboardInterrupt):
            sys.__excepthook__(exc_type, exc_value, exc_traceback)
            return
        
        # 记录错误信息
        error_msg = ''.join(traceback.format_exception(exc_type, exc_value, exc_traceback))
        print(f"严重错误: {error_msg}")
        
        try:
            # 显示错误弹窗
            messagebox.showerror(
                "程序错误",
                f"程序发生了一个未预期的错误:\n{str(exc_value)}\n\n详细信息已记录到日志文件中。"
            )
        except:
            # 如果弹窗失败,至少打印错误信息
            print(f"严重错误: {error_msg}")
    
    # 设置全局异常处理器
    sys.excepthook = handle_exception

def global_init():
    """全局初始化"""
    setup_global_exception_handler()

4. 配置管理优化

集中配置管理

# app/Config.py
# 网络配置
NETWORK_CONFIG = {
    'timeout': 10,           # 请求超时时间(秒)
    'max_retries': 3,        # 最大重试次数
    'max_workers': 5,        # 最大并发线程数
    'batch_timeout': 30,     # 批量操作超时时间
    'single_timeout': 10,    # 单个操作超时时间
}

# API请求频率控制配置
API_RATE_LIMIT = {
    'rank_query_delay': 0.1,      # 段位查询间隔(秒)
    'hidden_score_delay': 0.2,    # 隐藏分查询间隔(秒)
    'enable_rate_limit': True,    # 是否启用频率限制
}

优化效果

1. 稳定性提升

2. 用户体验改善

3. 性能优化

关键技术点

1. 线程池(ThreadPoolExecutor)

2. 队列(Queue)

3. 超时机制

4. 线程安全UI更新

总结

通过以上优化方案,我们成功解决了Python多线程应用中的卡死问题:

这些优化不仅解决了卡死问题,还提升了程序的稳定性、性能和用户体验,为后续功能扩展奠定了良好的基础。

到此这篇关于Python多线程应用中的卡死问题优化方案指南的文章就介绍到这了,更多相关Python多线程开发优化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文