我试图在两个线程之间发送一些数据,但需要5-10秒才能在另一个线程上接收到。主线程正在运行一个GUI(用egui编写),当用户单击一个按钮时,它会产生第二个线程,在进程中创建一个MPSC通道。第二个线程打开一个摄像头,并尝试使用openCV解码一个QR码,如果它检测到一个大于零的字符串长度,它会将其发送回主线程。然后主线程将其通道的一端丢弃,这将导致另一个线程结束。
我已经设置了一个“心跳”消息,在一秒钟内通过,但每当我真正检测到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++代码不共享线程时间的结果吗?
我已经在调试和发布模式下尝试过了,没有变化。
1条答案
按热度按时间hmtdttj41#
这个问题原来是我自己对egui图书馆的误解。事实证明,egui绘制线程(合理地)只在事件发生时重绘,例如鼠标/键盘交互。在这种情况下,我必须触发一个事件,以便egui draw函数可以拾取消息。
请注意egui文档的“CPU使用率”部分确实明确说明了这一点,但我错过了它:
EGUI仅在存在交互时重新绘制(例如,鼠标移动)或动画,因此如果您的应用处于空闲状态,也不会浪费CPU。
在egui::Context中有一个
request_repaint()
函数(注意文档中声明它在'Frame'中,但似乎不再是这种情况),所以我必须将我的app_ctx传递到QR线程中。ctx
的克隆成本很低,并且在内部使用引用计数,所以没有问题。这解决了我遇到的延迟问题,我希望这篇文章可以帮助其他人(像我这样没有充分阅读文档的人)在未来。