【问题标题】:Calling OpenCV function Canny() inside new thread causes segmentation fault在新线程中调用 OpenCV 函数 Canny() 会导致分段错误
【发布时间】:2019-02-03 21:58:54
【问题描述】:

我正在开发一个服务器应用程序,它基于客户端请求使用 OpenCV 库执行某些图像处理操作。应用程序的性质要求使用多个线程。最近我一直在处理一个非常顽固的错误,它导致了段错误。我能够将发生分段错误的代码部分归零。

这是最小的实现。

#include <iostream>
#include <thread>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>

int main() {
  cv::Mat img_input = cv::Mat::zeros(cv::Size(240, 320), CV_8UC1);
  cv::Mat img_output = cv::Mat::zeros(cv::Size(240, 320), CV_8UC1);

  int i = 1;
  while (i < 100) {
    std::cout << "- - - - - - - - - - - - - - " << i++ << std::endl;
    std::thread([&]() {
        std::cout << "Thread started." << std::endl;
        cv::Canny(img_input, img_output, 10, 20);
        std::cout << "Thread finished." << std::endl;
    }).join();

    std::cout << "Thread joined." << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(150));
  }

  return 0;
}

程序以 2 个不同的输出失败。有了这个输出...

- - - - - - - - - - - - - - 1
Thread started.
Thread finished.
Thread joined.
- - - - - - - - - - - - - - 2
Thread started.
Segmentation fault (core dumped)

.. 或者用这个。

- - - - - - - - - - - - - - 1
Thread started.
Thread finished.
Thread joined.
- - - - - - - - - - - - - - 2
Thread started.
Thread finished.
Segmentation fault (core dumped)

让我分享我的其他发现。段错误仅发生在我安装了 OpenCV 版本 3.3.0-dev 的嵌入式 linux 设备(Toradex Colibri iMX6 - 计算机模块)上。仅当我在新线程中使用 Canny() 函数时才会导致段错误。我也尝试过调用其他 OpenCV 函数,但它们都没有产生任何错误。

当我在我的 PC(Ubuntu 16.04,OpenCV 版本 3.3.0)上运行程序时,没有发生段错误。

有什么想法吗?

****** 更新 1 ******

我尝试听从 cmets 的一些建议。但是问题仍然存在。

我将 Mat 变量移动到每个线程的范围内,以使它们成为线程的本地变量。我还添加了一些额外的睡眠时间来等待线程完成。

这是我的新实现。

#include <iostream>
#include <thread>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>


void run() {
  cv::Mat img_input = cv::Mat::zeros(cv::Size(240, 320), CV_8UC1);
  cv::Mat img_output = cv::Mat::zeros(cv::Size(240, 320), CV_8UC1);

  std::cout << "Thread started." << std::endl;
  cv::Canny(img_input, img_output, 10, 20);
  std::cout << "Thread finished." << std::endl;
  std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

int main() {
  int i = 1;
  while (i < 100) {
    std::cout << "- - - - - - - - - - - - - - " << i++ << std::endl;
    std::thread t1(run);

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    std::cout << "Waiting to join thread." << std::endl;
    t1.join();
    std::cout << "Thread joined." << std::endl;

    std::this_thread::sleep_for(std::chrono::milliseconds(100));
  }

  return 0;
}

输出再次在...之间变化

- - - - - - - - - - - - - - 1
Thread started.
Thread finished.
Waiting to join thread.
Thread joined.
- - - - - - - - - - - - - - 2
Thread started.
Thread finished.
Segmentation fault (core dumped)

...和...

- - - - - - - - - - - - - - 1
Thread started.
Thread finished.
Waiting to join thread.
Thread joined.
- - - - - - - - - - - - - - 2
Thread started.
Segmentation fault (core dumped)

****** 更新 2 ******

我发现了一个类似问题的帖子here。建议在代码中添加以下行可以解决问题。

cv::setNumThreads(0);

我在 main() 函数的开头添加了这一行,似乎它解决了分段错误的问题。这是否有助于解释程序发生了什么?

目前看来,这似乎是一个很好的快速解决方案,但在我接受它作为合适的解决方案之前,我想了解它的背景。谁能解释为什么不再发生段错误?

有没有更好的解决方案?

【问题讨论】:

  • mat 变量可能需要互斥锁,以便一个线程在使用时不会覆盖。但是为了确保并行性,最好使用vector<:mat>(每个线程独立处理cv::Mat)并且线程不应该阻塞(不要立即加入)。
  • 感谢您的建议。我尝试了您的一些建议(请参阅更新的问题)。我将 Mat 变量移到线程范围内,因为我不需要在线程之间共享此变量。我没有使用任何互斥锁,因为我假设在此设置中我不需要它们。我还添加了一些睡眠来强制线程不会立即加入。不幸的是,这些更改没有帮助。
  • 为什么不gdb 看看发生了什么?
  • @xiaobing,我使用 gdb 调试器找到了失败的代码部分。当程序失败时,程序将在以下反汇编程序行停止。 0x767d5770 04 30 12 e5 ldr r3, [r2, #-4] 我不知道如何帮助自己处理这些信息。你能指出我正确的方向吗?
  • “OpenCV version 3.3.0-dev”——这闻起来很像在 3.3.0 发布后的某个时候从 master 分支构建的不稳定版本。可以试试稳定版吗?您可以尝试使用其他版本(在同一硬件上)吗?

标签: c++ linux multithreading opencv segmentation-fault


【解决方案1】:

这是一个评论,但我缺少相应的权利。很抱歉。

这个错误很可能不是由调用 OpenCV 引起的,而是由正在使用的线程工具引起的。一种可能性是您在 while 循环中使用临时 std::thread 对象。试着给它一个名字,例如

std::thread worker([]...);
worker.join()

由于上述更改没有帮助,问题也可能在较低级别(例如,库冲突)。

【讨论】:

  • 感谢您的评论。我重新排列了代码以遵循您的建议(请参阅更新的问题),但不幸的是我仍然遇到了段错误。
  • 太糟糕了。由于新版本中没有更多的数据共享,您的问题应该有所不同。我会尝试在这里调查类似的问题,例如,stackoverflow.com/questions/9002264/…
  • 我发现明确告诉 OpenCV 库禁用线程优化(调用函数 cv::setNumThreads(0); )可以防止段错误。你可能知道为什么这有帮助吗?我用这些附加信息更新了原始帖子。
  • 这真的很有趣!您能否检查一下您的嵌入式和桌面构建使用了哪个并行框架? cv::getBuildInformation() 应该提供信息
  • 并行框架(桌面):TBB(ver 4.4 interface 9002) 并行框架(嵌入式):TBB(ver 2017.0 interface 9106)整体输出:(桌面):text-share.com/view/d7c75635#7KK2WC6wbZsF833dFAF4zXxZb0pzInJG(嵌入式):@987654323 @
【解决方案2】:

事实证明,将线程数设置为 0 或 1 即可解决问题(不再出现段错误)。这是通过以下两行代码之一完成的。

cv::setNumThreads(0); // Setting the number of thread to 0.
cv::setNumThreads(1); // Setting the number of thread to 1.

我不太了解 OpenCV 库,无法理解为什么会发生段错误以及为什么这条线可以解决问题,但此时我对结果感到满意,并将此问题标记为已回答。希望这对其他人有帮助。

【讨论】:

猜你喜欢
  • 2019-11-21
  • 2015-01-18
  • 2021-05-25
  • 1970-01-01
  • 1970-01-01
  • 2021-12-13
  • 2020-09-26
  • 1970-01-01
  • 2017-10-02
相关资源
最近更新 更多