使用 MPSC 通道在线程之间发送数据有时需要 5-10 秒,可能线程饥饿?

问题描述 投票:0回答:1

我尝试在两个线程之间发送一些数据,但另一个线程需要 5-10 秒才能接收到该数据。主线程正在运行 GUI(用 egui 编写),当用户单击按钮时,它会生成第二个线程,在进程中创建 MPSC 通道。第二个线程打开摄像头并尝试使用 openCV 解码 QR 码,如果检测到字符串长度大于零,则会将其发送回主线程。然后主线程丢弃其通道末端,这会导致另一个线程结束。

我已经设置了一条“心跳”消息,该消息在一秒钟内就会通过,但每当我实际检测到二维码并发送字符串时,都需要很长时间才能到达另一个线程。所有输出都会暂停,我得到多个“发送”字符串的实例,但主线程没有调试输出表明它直到 5-10 秒后才收到它。

这是我在第二个线程中运行的代码,该线程似乎获得了所有的CPU时间(我也尝试用它创建一个openCV窗口并显示输出,并且它继续快速更新,因此该线程仍然是运行良好):

pub enum QRThreadResponse {
    NewQRString(String),
    ThreadError(String),
    Heartbeat,
    ThreadShutdown,
}


pub fn SpawnQrThread(camera_id: i32, chl: Sender<QRThreadResponse>){
    thread::spawn(move || {
        println!("Hello from QR Thread");
        let mut qr_detector = objdetect::QRCodeDetector::default().expect("FAILED to create QR Detector");
        let mut res = types::VectorOfPoint::new();
        let mut camera = videoio::VideoCapture::new(camera_id, videoio::CAP_DSHOW).expect("Open Camera Failed");
        let mut img = Mat::default();
        let mut recqr = Mat::default();

        loop{
            match camera.read(&mut img) {
                Err(_) => break,
                _ => (),
            };
            let ret = qr_detector.detect_and_decode(&img, &mut res, &mut recqr).expect("QR DETECT ERROR");
            let s = String::from_utf8_lossy(&ret);
            if s.len() > 0 {
                println!("SENDING: {:?}", s);
                match chl.send(QRThreadResponse::NewQRString(s.to_string())){
                    Err(_) => {println!("QUITTING QR THREAD NQS"); break},
                    _ => (),
                }
            }

            match chl.send(QRThreadResponse::Heartbeat){
                Err(_) => {println!("QUITTING QR THREAD HB"); break},
                _ => (),
            }
        }

    });
    ()
}

在我的主 GUI 线程中,我只需在按下按钮时调用此函数:

    fn spawn_camera_thread(&mut self) {
        if self.QRRecieveThread.is_none() {
            let (snd, rcv) = channel();
            SpawnQrThread(0, snd);
            self.QRRecieveThread = Some(rcv);
        }
    }

在 GUI 线程中我检查响应:

if let Some(qr_thread) = &self.QRRecieveThread{
    if let Ok(qr_response) = qr_thread.recv_timeout(Duration::from_millis(1)) {
        match qr_response {
            QRThreadResponse::NewQRString(data) => {
                self.update_activity_log(data);
                self.app_state = AppState::NewItemScanned;
                self.kill_qr_thread();
            },
            QRThreadResponse::Heartbeat => println!("Got Heartbeat"),
            _ => (),
        }
    }
}

我如何找出为什么线程之间发送数据需要这么长时间?这是否只是 openCV 绑定和底层 C++ 代码不共享线程时间的结果?

我已经在调试和发布模式下尝试过,没有变化。

multithreading opencv rust channel egui
1个回答
0
投票

问题原来是我自己对egui库的误解。事实证明,egui 绘制线程(明智地)仅在发生事件(例如鼠标/键盘交互)时重绘。在这种情况下,我必须触发一个事件,以便 egui 绘制函数可以获取消息。

请注意,egui 文档的“CPU 使用率”部分确实明确说明了这一点,但我错过了:

egui 仅在存在交互(例如鼠标移动)或动画时重新绘制,因此如果您的应用程序处于空闲状态,则不会浪费 CPU。

egui::Context 中有一个

request_repaint()
函数(请注意,文档指出它位于“Frame”内,但情况似乎不再如此),因此我必须将 app_ctx 传递到 QR 线程中。
ctx
克隆成本低廉,并且在内部使用引用计数,因此没有问题。

fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
    ...
    self.spawn_camera_thread(ctx.clone());
    ...
}

fn spawn_camera_thread(&mut self, gui: egui::Context) {
   if self.QRRecieveThread.is_none() {
        let (snd, rcv) = channel();
        self.QRRecieveThread = Some(rcv);
        spawn_qr_thread(0, snd, gui);
    }
}

pub fn spawn_qr_thread(camera_id: i32, chl: Sender<QRThreadResponse>, gui: Context){
    //QR Detection Code
    match chl.send(QRThreadResponse::NewQRString(s.to_string())){
          Err(_) => {println!("QUITTING QR THREAD"); break},
          _ => gui.request_repaint(),
    }
}

这解决了我遇到的延迟问题,我希望这篇文章可以帮助其他人(像我一样没有充分阅读文档的人)将来。

© www.soinside.com 2019 - 2024. All rights reserved.