You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CAEN DT5742采集程序单核心100%CPU无响应问题求助

CAEN DT5742数字化仪采集进程CPU占满问题排查与处理

问题背景

使用CAEN DT5742 16通道数字化仪开展测量工作,基于CAENPy(CAENDigitizer库的Python封装)开发多进程程序,通过步进电机控制激光扫描区域,同时读取模拟读出板数据。程序运行整体正常,但会随机出现无响应情况,且两个进程会占用单个核心100%的CPU资源。通过py-spy性能分析定位到问题根源在CAENPy库的_GetNumEvents方法。

核心采集代码

def read_and_save_events(self, max_num_events: int = 1):
    """Reads a specified number of events from the digitizer.

    Arguments
    ---------
    max_num_events: int, default 1
        Number of events to read.

    Returns
    -------
    nevts: int
        Number of events read.
    """
    nevts: int = 0
    data = []
    retries = 0
    while retries < MAX_RETRIES:
        retries += 1
        try:
            with self.device:
                self.log.info("Reading %d events...", max_num_events)
                while nevts < max_num_events:
                    time.sleep(0.05)
                    waveforms = self.get_waveforms()
                    current_nevts = len(waveforms)
                    nevts += current_nevts
                    data += waveforms
                    self.log.info(
                        "Read %d out of %d events...", nevts, max_num_events
                    )
            break
        except RuntimeError:
            self.log.error("Encountered error during read. Retrying...")
            self.hard_reset(self._device_id)
            self.close()
            self.device = CAEN_DT5742_Digitizer(self._device_id)
            self.init()
            time.sleep(RETRY_TIMEOUT)
    else:
        self.log.error("Too many retries, aborting read...")

    if self._save_path is None:
        self.log.warning("No save path specified, waveforms not saved!")
        return 0

    # Disentangle data and save to file
    df = pd.DataFrame(data)
    timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
    data_file = os.path.join(self._save_path, f"waveforms_{timestamp}.h5")
    self.curr_savefile = data_file

    with pd.HDFStore(data_file, "w") as store:
        for channel in df.columns:
            channel_df = []
            for eventid, event in enumerate(df[channel]):
                event_df = pd.DataFrame(event)
                for column in event_df.columns:
                    col = pd.Series(
                        event_df[column].values,
                        name=f"{eventid}_{column.split()[0]}",
                    )
                    channel_df.append(col)
            channel_df = pd.concat(channel_df, axis=1)
            store.put(channel, channel_df)

修复可能性判断

  1. 开源库场景:如果CAENPy是开源项目,可直接查看_GetNumEvents的实现代码,排查是否存在死循环、未处理异常或无超时的阻塞逻辑,针对性修复后重新编译安装库;若该方法是对CAENDigitizer官方C库的封装,可查阅官方文档确认是否存在已知Bug,尝试升级库版本,或向官方提交Issue反馈问题。
  2. 闭源库场景:无法直接修改底层库的Bug,只能通过上层程序逻辑规避或监控处理。

规避与监控方案

1. 进程级CPU占用监控与重启

通过psutil库监控采集进程的CPU使用率,当进程持续100%占用核心超过设定阈值(如30秒)时,强制终止并重启该进程,同时重新初始化数字化仪设备。

示例逻辑:

import psutil
import time

def monitor_worker_process(pid, threshold=30):
    while True:
        try:
            proc = psutil.Process(pid)
            cpu_percent = proc.cpu_percent(interval=1)
            if cpu_percent >= 99:
                # 多次确认避免误判
                consecutive_high = 0
                for _ in range(5):
                    if proc.cpu_percent(interval=1) >=99:
                        consecutive_high +=1
                if consecutive_high >=5:
                    proc.terminate()
                    proc.wait()
                    # 执行采集进程重启逻辑
                    restart_acquisition_process()
                    break
            time.sleep(5)
        except psutil.NoSuchProcess:
            break

2. 采集逻辑添加心跳超时检测

在采集循环中添加心跳检测,若长时间未读取到新事件(如10秒),主动抛出异常触发重置流程:

修改内层采集循环:

import datetime

NO_EVENT_TIMEOUT = 10  # 10秒无事件触发超时
last_event_time = datetime.datetime.now()

while nevts < max_num_events:
    time.sleep(0.05)
    waveforms = self.get_waveforms()
    current_nevts = len(waveforms)
    
    if current_nevts > 0:
        last_event_time = datetime.datetime.now()
    
    nevts += current_nevts
    data += waveforms
    self.log.info("Read %d out of %d events...", nevts, max_num_events)
    
    # 检查超时
    elapsed = (datetime.datetime.now() - last_event_time).total_seconds()
    if elapsed > NO_EVENT_TIMEOUT:
        self.log.error(f"No events received for {elapsed}s, triggering reset")
        raise RuntimeError("Event acquisition timeout")

3. 封装采集方法的超时控制

将get_waveforms()调用放到子线程中,主线程设置超时等待,超时则终止子线程并抛出异常:

import threading

def _get_waveforms_with_timeout(self, timeout=10):
    result = []
    exception = None
    
    def target():
        nonlocal result, exception
        try:
            result = self.get_waveforms()
        except Exception as e:
            exception = e
    
    thread = threading.Thread(target=target)
    thread.start()
    thread.join(timeout=timeout)
    
    if thread.is_alive():
        raise RuntimeError("Waveform read timed out")
    if exception is not None:
        raise exception
    return result

在采集循环中替换原waveforms = self.get_waveforms()为waveforms = self._get_waveforms_with_timeout()。

4. 强化设备重置逻辑

在触发重置时,不仅重启设备对象,还可调用设备的硬件重置接口(若有),确保设备回到初始状态后再重新初始化。

内容的提问来源于stack exchange,提问作者TensaZangetsu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 12:14:53