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

xxe27gdn  于 2023-10-20  发布在  其他
关注(0)|答案(1)|浏览(147)

我试图在两个线程之间发送一些数据,但需要5-10秒才能在另一个线程上接收到。主线程正在运行一个GUI(用egui编写),当用户单击一个按钮时,它会产生第二个线程,在进程中创建一个MPSC通道。第二个线程打开一个摄像头,并尝试使用openCV解码一个QR码,如果它检测到一个大于零的字符串长度,它会将其发送回主线程。然后主线程将其通道的一端丢弃,这将导致另一个线程结束。
我已经设置了一个“心跳”消息,在一秒钟内通过,但每当我真正检测到QR码并发送字符串时,它需要很长时间才能通过另一个线程。所有的输出都暂停了,我得到了它“发送”字符串的多个示例,但直到5-10秒后,主线程才发出调试输出说它收到了字符串。
这是我在第二个线程中运行的代码,这个线程似乎占用了所有的CPU时间(我也尝试过用它创建一个openCV窗口并显示输出,并且继续快速更新,所以线程仍然运行良好):

  1. pub enum QRThreadResponse {
  2. NewQRString(String),
  3. ThreadError(String),
  4. Heartbeat,
  5. ThreadShutdown,
  6. }
  7. pub fn SpawnQrThread(camera_id: i32, chl: Sender<QRThreadResponse>){
  8. thread::spawn(move || {
  9. println!("Hello from QR Thread");
  10. let mut qr_detector = objdetect::QRCodeDetector::default().expect("FAILED to create QR Detector");
  11. let mut res = types::VectorOfPoint::new();
  12. let mut camera = videoio::VideoCapture::new(camera_id, videoio::CAP_DSHOW).expect("Open Camera Failed");
  13. let mut img = Mat::default();
  14. let mut recqr = Mat::default();
  15. loop{
  16. match camera.read(&mut img) {
  17. Err(_) => break,
  18. _ => (),
  19. };
  20. let ret = qr_detector.detect_and_decode(&img, &mut res, &mut recqr).expect("QR DETECT ERROR");
  21. let s = String::from_utf8_lossy(&ret);
  22. if s.len() > 0 {
  23. println!("SENDING: {:?}", s);
  24. match chl.send(QRThreadResponse::NewQRString(s.to_string())){
  25. Err(_) => {println!("QUITTING QR THREAD NQS"); break},
  26. _ => (),
  27. }
  28. }
  29. match chl.send(QRThreadResponse::Heartbeat){
  30. Err(_) => {println!("QUITTING QR THREAD HB"); break},
  31. _ => (),
  32. }
  33. }
  34. });
  35. ()
  36. }

在我的主GUI线程中,当按钮被按下时,我简单地调用这个函数:

  1. fn spawn_camera_thread(&mut self) {
  2. if self.QRRecieveThread.is_none() {
  3. let (snd, rcv) = channel();
  4. SpawnQrThread(0, snd);
  5. self.QRRecieveThread = Some(rcv);
  6. }
  7. }

在GUI线程中,我检查响应:

  1. if let Some(qr_thread) = &self.QRRecieveThread{
  2. if let Ok(qr_response) = qr_thread.recv_timeout(Duration::from_millis(1)) {
  3. match qr_response {
  4. QRThreadResponse::NewQRString(data) => {
  5. self.update_activity_log(data);
  6. self.app_state = AppState::NewItemScanned;
  7. self.kill_qr_thread();
  8. },
  9. QRThreadResponse::Heartbeat => println!("Got Heartbeat"),
  10. _ => (),
  11. }
  12. }
  13. }

我如何弄清楚为什么在线程之间发送数据要花这么长时间?这可能只是openCV绑定和底层C++代码不共享线程时间的结果吗?
我已经在调试和发布模式下尝试过了,没有变化。

hmtdttj4

hmtdttj41#

这个问题原来是我自己对egui图书馆的误解。事实证明,egui绘制线程(合理地)只在事件发生时重绘,例如鼠标/键盘交互。在这种情况下,我必须触发一个事件,以便egui draw函数可以拾取消息。
请注意egui文档的“CPU使用率”部分确实明确说明了这一点,但我错过了它:
EGUI仅在存在交互时重新绘制(例如,鼠标移动)或动画,因此如果您的应用处于空闲状态,也不会浪费CPU。
在egui::Context中有一个request_repaint()函数(注意文档中声明它在'Frame'中,但似乎不再是这种情况),所以我必须将我的app_ctx传递到QR线程中。ctx的克隆成本很低,并且在内部使用引用计数,所以没有问题。

  1. fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
  2. ...
  3. self.spawn_camera_thread(ctx.clone());
  4. ...
  5. }
  6. fn spawn_camera_thread(&mut self, gui: egui::Context) {
  7. if self.QRRecieveThread.is_none() {
  8. let (snd, rcv) = channel();
  9. self.QRRecieveThread = Some(rcv);
  10. spawn_qr_thread(0, snd, gui);
  11. }
  12. }
  13. pub fn spawn_qr_thread(camera_id: i32, chl: Sender<QRThreadResponse>, gui: Context){
  14. //QR Detection Code
  15. match chl.send(QRThreadResponse::NewQRString(s.to_string())){
  16. Err(_) => {println!("QUITTING QR THREAD"); break},
  17. _ => gui.request_repaint(),
  18. }
  19. }

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

展开查看全部

相关问题