signal_pending(current)用法

本文深入探讨了信号处理机制中的signal_pending函数,详细解释了如何通过该函数检查当前进程是否有待处理的信号。此外,还特别介绍了当信号处理完毕后如何根据-ERESTARTSYS属性决定是否重新执行被信号中断的系统调用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

signal_pending(current)

检查当前进程是否有信号处理,返回不为0表示有信号需要处理。
返回 -ERESTARTSYS 表示信号函数处理完毕后重新执行信号函数前的某个系统调用。也就是说,如果信号函数前有发生系统调用,在调度信号处理函数之前,内核会检查系统调用的返回值,看看是不是因为这个信号而中断了系统调用.

如果返回值-ERESTARTSYS,并且当前调度的信号具备-ERESTARTSYS属性,系统就会在用户信号函数返回之后再执行该系统调用。 

import logging import time from enum import Enum from typing import List, Dict, Optional, Any from threading import Lock from dataclasses import dataclass from PySide6.QtWidgets import QWidget, QVBoxLayout from PySide6.QtCore import Slot, Signal, QTimer from lightweight_charts.widgets import QtChart # 配置日志系统 logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) class ChartState(Enum): """图表状态枚举""" INITIALIZING = "initializing" WEBVIEW_LOADING = "webview_loading" CHART_LOADING = "chart_loading" READY = "ready" ERROR = "error" @dataclass class ChartConfig: """图表配置类""" # 重试配置 max_retries: int = 5 initial_retry_interval: int = 500 # 毫秒 max_retry_interval: int = 5000 # 毫秒 # 缓存配置 max_pending_updates: int = 100 cache_cleanup_interval: int = 30 # 秒 max_cache_age: int = 300 # 秒 # 性能配置 batch_update_threshold: int = 10 # JavaScript检测配置 js_ready_check_interval: int = 100 # 毫秒 js_ready_max_attempts: int = 50 class DataValidator: """数据验证器""" @staticmethod def validate_historical_data(klines: List[Any]) -> bool: """验证历史K线数据格式""" if not klines or not isinstance(klines, list): return False try: for kline in klines: if not isinstance(kline, (list, tuple)) or len(kline) < 6: return False # 检查数值类型 for i in range(1, 6): # open, high, low, close, volume float(kline[i]) # 检查时间戳 int(kline[0]) return True except (ValueError, TypeError, IndexError): return False @staticmethod def validate_kline_update(kline: Dict[str, Any]) -> bool: """验证实时K线更新数据格式""" required_keys = ['t', 'o', 'h', 'l', 'c', 'v'] if not isinstance(kline, dict): return False try: for key in required_keys: if key not in kline: return False # 验证数值 float(kline['o']) # open float(kline['h']) # high float(kline['l']) # low float(kline['c']) # close float(kline['v']) # volume int(kline['t']) # timestamp # 验证价格逻辑(high >= max(open,close), low <= min(open,close)) o, h, l, c = float(kline['o']), float(kline['h']), float(kline['l']), float(kline['c']) if h < max(o, c) or l > min(o, c): logger.warning("K线数据价格逻辑不一致") return False return True except (ValueError, TypeError, KeyError): return False class LightweightChartWidget(QWidget): """ 一个使用 lightweight-charts 库来显示高性能金融图表的封装组件。 """ # 信号定义 chartReady = Signal() chartError = Signal(str) # 新增错误信号 stateChanged = Signal(str) # 新增状态变化信号 def __init__(self, symbol: str, interval: str, config: Optional[ChartConfig] = None, parent=None): super().__init__(parent) self.symbol = symbol self.interval = interval self.config = config or ChartConfig() # 状态管理 self._state = ChartState.INITIALIZING self._state_lock = Lock() self._is_destroyed = False # 销毁标志 # 数据缓存(增加时间戳) self._pending_historical_data: Optional[List[Any]] = None self._pending_updates: List[Dict[str, Any]] = [] self._cache_timestamps: List[float] = [] # 重试机制 self._retry_count = 0 self._current_retry_interval = self.config.initial_retry_interval # JavaScript就绪检测 self._js_ready_attempts = 0 self._js_ready_timer = QTimer() self._js_ready_timer.timeout.connect(self._check_javascript_ready) # 缓存清理定时器 self._cache_cleanup_timer = QTimer() self._cache_cleanup_timer.timeout.connect(self._cleanup_old_cache) self._cache_cleanup_timer.start(self.config.cache_cleanup_interval * 1000) # 性能优化:批量更新 self._batch_updates: List[Dict[str, Any]] = [] self._batch_timer = QTimer() self._batch_timer.setSingleShot(True) self._batch_timer.timeout.connect(self._process_batch_updates) self._setup_ui() self._set_state(ChartState.WEBVIEW_LOADING) def _setup_ui(self): """设置用户界面""" self.layout = QVBoxLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) self.chart = QtChart(widget=self, inner_width=1, inner_height=1) self.webview = self.chart.get_webview() self.layout.addWidget(self.webview) # 应用样式配置 self._apply_chart_styles() # 连接WebView信号 self.webview.loadFinished.connect(self._on_webview_loaded) def _apply_chart_styles(self): """应用图表样式配置""" try: self.chart.layout( background_color='#131722', text_color='#D9D9D9', font_size=12 ) self.chart.grid( vert_enabled=True, horz_enabled=True, color='#3C4048', style='solid' ) self.chart.candle_style( up_color='#26a69a', down_color='#ef5350', border_up_color='#26a69a', border_down_color='#ef5350', wick_up_color='#26a69a', wick_down_color='#ef5350' ) self.chart.volume_config( up_color='rgba(38, 166, 154, 0.5)', down_color='rgba(239, 83, 80, 0.5)' ) except Exception as e: logger.error(f"应用图表样式时发生错误: {e}") def _set_state(self, new_state: ChartState): """线程安全的状态设置""" if getattr(self, '_is_destroyed', False): return with self._state_lock: if self._state != new_state: old_state = self._state self._state = new_state logger.info(f"[{self.symbol}] 状态变化: {old_state.value} -> {new_state.value}") try: self.stateChanged.emit(new_state.value) except (RuntimeError, AttributeError): # 信号可能已经断开 pass def get_state(self) -> ChartState: """获取当前状态""" with self._state_lock: return self._state @Slot(bool) def _on_webview_loaded(self, success: bool): """WebView加载完成处理""" if success: logger.info(f"[{self.symbol}] WebView加载成功,开始检测JavaScript就绪状态") self._set_state(ChartState.CHART_LOADING) self._reset_retry_state() self._start_javascript_ready_check() else: self._handle_load_failure() def _start_javascript_ready_check(self): """开始JavaScript就绪状态检测""" self._js_ready_attempts = 0 self._js_ready_timer.start(self.config.js_ready_check_interval) def _check_javascript_ready(self): """检测JavaScript图表库是否就绪""" self._js_ready_attempts += 1 # 检测代码:验证图表对象是否存在并可用 js_code = """ (function() { try { return window.chart && typeof window.chart === 'object' && typeof window.chart.update === 'function'; } catch(e) { return false; } })(); """ def on_result(result): if result: self._on_javascript_ready() elif self._js_ready_attempts >= self.config.js_ready_max_attempts: self._on_javascript_timeout() try: # 使用WebView的页面执行JavaScript self.webview.page().runJavaScript(js_code, on_result) except Exception as e: logger.warning(f"JavaScript就绪检测失败: {e}") if self._js_ready_attempts >= self.config.js_ready_max_attempts: self._on_javascript_timeout() def _on_javascript_ready(self): """JavaScript就绪处理""" self._js_ready_timer.stop() self._set_state(ChartState.READY) logger.info(f"[{self.symbol}] 图表完全就绪") # 发射就绪信号 self.chartReady.emit() # 处理缓存数据 self._process_cached_data() def _on_javascript_timeout(self): """JavaScript检测超时处理""" self._js_ready_timer.stop() error_msg = f"图表JavaScript初始化超时 (尝试了 {self._js_ready_attempts} 次)" logger.error(f"[{self.symbol}] {error_msg}") self._set_state(ChartState.ERROR) self.chartError.emit(error_msg) def _handle_load_failure(self): """处理加载失败""" self._retry_count += 1 error_msg = f"WebView加载失败 (尝试 {self._retry_count}/{self.config.max_retries})" logger.error(f"[{self.symbol}] {error_msg}") if self._retry_count < self.config.max_retries: # 指数退避重试 self._current_retry_interval = min( self._current_retry_interval * 2, self.config.max_retry_interval ) QTimer.singleShot(self._current_retry_interval, lambda: self.webview.reload()) else: error_msg = f"达到最大重试次数,加载失败" logger.critical(f"[{self.symbol}] {error_msg}") self._set_state(ChartState.ERROR) self.chartError.emit(error_msg) def _reset_retry_state(self): """重置重试状态""" self._retry_count = 0 self._current_retry_interval = self.config.initial_retry_interval def _process_cached_data(self): """处理缓存的数据""" try: # 处理历史数据 if self._pending_historical_data is not None: self._apply_historical_data(self._pending_historical_data) self._pending_historical_data = None # 处理缓存的更新 if self._pending_updates: for kline in self._pending_updates: self._apply_kline_update_internal(kline) self._clear_cache() except Exception as e: logger.error(f"[{self.symbol}] 处理缓存数据时发生错误: {e}") @Slot(list) def set_historical_data(self, klines: List[Any]): """设置历史数据""" if getattr(self, '_is_destroyed', False): return if not DataValidator.validate_historical_data(klines): error_msg = "历史K线数据格式验证失败" logger.error(f"[{self.symbol}] {error_msg}") try: self.chartError.emit(error_msg) except (RuntimeError, AttributeError): pass return if self.get_state() != ChartState.READY: logger.info(f"[{self.symbol}] 图表未就绪,缓存历史数据 ({len(klines)} 条)") self._pending_historical_data = klines return self._apply_historical_data(klines) def _apply_historical_data(self, klines: List[Any]): """应用历史数据到图表""" try: logger.info(f"[{self.symbol}] 处理 {len(klines)} 条历史K线数据") # 优化的数据处理:避免不必要的列操作 chart_data = [{ 'time': int(kline[0]), 'open': float(kline[1]), 'high': float(kline[2]), 'low': float(kline[3]), 'close': float(kline[4]), 'volume': float(kline[5]) } for kline in klines] self.chart.set(chart_data) logger.info(f"[{self.symbol}] 历史数据加载成功") except Exception as e: error_msg = f"处理历史数据时发生错误: {e}" logger.error(f"[{self.symbol}] {error_msg}") self.chartError.emit(error_msg) @Slot(dict) def update_kline(self, kline: Dict[str, Any]): """更新K线数据""" if getattr(self, '_is_destroyed', False): return if not DataValidator.validate_kline_update(kline): logger.warning(f"[{self.symbol}] 实时K线数据格式验证失败") return if self.get_state() != ChartState.READY: self._cache_update(kline) return # 批量处理优化 self._batch_updates.append(kline) if len(self._batch_updates) >= self.config.batch_update_threshold: self._process_batch_updates() else: # 延迟处理,允许批量积累 try: if hasattr(self, '_batch_timer') and not self._batch_timer.isActive(): self._batch_timer.start(50) # 50ms延迟 except (RuntimeError, AttributeError): # 定时器可能已被删除,直接处理 self._process_batch_updates() def _cache_update(self, kline: Dict[str, Any]): """缓存更新数据""" current_time = time.time() # 清理过期缓存 self._cleanup_old_cache() # 限制缓存大小 if len(self._pending_updates) >= self.config.max_pending_updates: # 移除最旧的数据 self._pending_updates.pop(0) self._cache_timestamps.pop(0) self._pending_updates.append(kline) self._cache_timestamps.append(current_time) def _process_batch_updates(self): """处理批量更新""" if not self._batch_updates: return try: # 处理最新的数据(通常只需要最后一条) latest_kline = self._batch_updates[-1] self._apply_kline_update_internal(latest_kline) self._batch_updates.clear() except Exception as e: logger.error(f"[{self.symbol}] 批量更新处理失败: {e}") def _apply_kline_update_internal(self, kline: Dict[str, Any]): """内部K线更新实现""" try: update_data = { 'time': pd.to_datetime(kline['t'], unit='ms'), 'open': float(kline['o']), 'high': float(kline['h']), 'low': float(kline['l']), 'close': float(kline['c']), 'volume': float(kline['v']) } kline_series = pd.Series(update_data) self.chart.update(kline_series) except Exception as e: logger.error(f"[{self.symbol}] 更新图表失败: {e}") def _cleanup_old_cache(self): """清理过期缓存""" if not self._cache_timestamps: return current_time = time.time() cutoff_time = current_time - self.config.max_cache_age # 找到第一个未过期的索引 valid_index = 0 for i, timestamp in enumerate(self._cache_timestamps): if timestamp >= cutoff_time: valid_index = i break else: valid_index = len(self._cache_timestamps) if valid_index > 0: self._pending_updates = self._pending_updates[valid_index:] self._cache_timestamps = self._cache_timestamps[valid_index:] logger.info(f"[{self.symbol}] 清理了 {valid_index} 条过期缓存") def _clear_cache(self): """清空所有缓存""" self._pending_updates.clear() self._cache_timestamps.clear() def force_refresh(self): """强制刷新图表""" logger.info(f"[{self.symbol}] 执行强制刷新") self._set_state(ChartState.WEBVIEW_LOADING) self._reset_retry_state() self.webview.reload() def get_cache_info(self) -> Dict[str, Any]: """获取缓存信息(用于调试)""" return { 'pending_updates_count': len(self._pending_updates), 'has_pending_historical': self._pending_historical_data is not None, 'batch_updates_count': len(self._batch_updates), 'state': self._state.value, 'retry_count': self._retry_count } def cleanup(self): """清理资源""" try: logger.info(f"[{self.symbol}] 开始清理资源") # 安全停止定时器 - 检查对象是否仍然有效 timers = [ ('js_ready_timer', self._js_ready_timer), ('cache_cleanup_timer', self._cache_cleanup_timer), ('batch_timer', self._batch_timer) ] for timer_name, timer in timers: try: if timer is not None and hasattr(timer, 'isActive') and timer.isActive(): timer.stop() logger.debug(f"[{self.symbol}] 已停止 {timer_name}") except (RuntimeError, AttributeError) as e: logger.debug(f"[{self.symbol}] {timer_name} 已被Qt清理: {e}") # 清理缓存 self._clear_cache() self._pending_historical_data = None logger.info(f"[{self.symbol}] 资源清理完成") except Exception as e: # 在析构过程中,日志系统可能也不可用,所以使用try-except try: logger.error(f"[{self.symbol}] 清理资源时发生错误: {e}") except: pass def __del__(self): """析构函数 - 安全版本""" try: if hasattr(self, '_is_destroyed') and not self._is_destroyed: if hasattr(self, '_pending_updates'): self._pending_updates.clear() if hasattr(self, '_cache_timestamps'): self._cache_timestamps.clear() if hasattr(self, '_batch_updates'): self._batch_updates.clear() self._pending_historical_data = None except: pass # 静默处理日志错误 def destroy_widget(self): """手动销毁组件(推荐使用此方法而非依赖析构函数)""" try: self.cleanup() try: self.chartReady.disconnect() self.chartError.disconnect() self.stateChanged.disconnect() if hasattr(self.webview, 'loadFinished'): self.webview.loadFinished.disconnect() except (RuntimeError, TypeError): pass # 信号可能已经断开或对象已删除 self._is_destroyed = True except Exception as e: try: logger.error(f"[{self.symbol}] 销毁组件时发生错误: {e}") except: pass 这样修复对吗?
最新发布
07-11
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值