【问题标题】:Boost.thread code presents different behaviour in Ubuntu and in WindowsBoost.thread 代码在 Ubuntu 和 Windows 中呈现不同的行为
【发布时间】:2012-12-04 11:17:06
【问题描述】:

我有一个简单的程序来测试我是否可以从不同的线程可视化点云并继续在主线程中工作,直到在终端中输入 'q'

在 Ubuntu 10.04 中,代码有效,让我可以在每次迭代中将新点添加到云中时可视化云。但是,在 Windows 7 中这不起作用(我正在使用 QtCreator 编译它)。显示云并在每一轮中计算新点,但这永远不会退出。键入 'q' 时,循环停止,但可视化线程继续运行。停止执行的唯一方法是显式使用 CTRL+C

更多内容:

  • 如果我没有在 Visualize 函数中的 !viewer->wasStopped() 循环之前取消注释 addPointCloud 行,点云从未显示。没关系,稍后在循环中我明确添加它。它必须在循环之前完成(现在该行已被注释以证明该行为)。
  • 我也尝试使用 boost::mutex 代替 *tbb::queuing_mutex*,但同样,程序不会退出。

你知道为什么线程永远不会加入吗?此外,总是欢迎对我的线程使用提出建设性批评,我希望不断改进。

代码如下:

#include <boost/thread/thread.hpp>
#include <iostream>
#include <pcl/point_types.h>
#include <pcl/visualization/pcl_visualizer.h>
#include "tbb/queuing_mutex.h"

typedef pcl::PointXYZ PointType;
typedef pcl::PointCloud<PointType> PointCloudType;
typedef tbb::queuing_mutex MutexType;
//typedef boost::mutex MutexType;
MutexType safe_update;

const unsigned int HEIGHT = 100;
const unsigned int WIDTH = 100;
bool has_to_update(true);

void Visualize(PointCloudType::Ptr cloud) {
  pcl::visualization::PCLVisualizer* viewer = new pcl::visualization::PCLVisualizer("Vis in thread",true);
  viewer->setBackgroundColor(1.0,0.0,0.0);
//  viewer->addPointCloud<PointType>(cloud, "sample cloud");
  viewer->addCoordinateSystem(1.0);
  viewer->initCameraParameters();
  viewer->resetCamera();
  while(!viewer->wasStopped()) {
    viewer->spinOnce(100);
    {
//      boost::lock_guard<MutexType> lock(safe_update);
        MutexType::scoped_lock lock(safe_update);
      if(has_to_update) {
        if(!viewer->updatePointCloud<PointType>(cloud, "sample cloud")) {
          viewer->addPointCloud<PointType>(cloud, "sample cloud");
          viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "sample cloud");
          viewer->resetCamera();
        }
        has_to_update = false;
      }
    } // end scoped_lock
  }
  delete viewer;
};

int main(int argc, char** argv) {
  PointCloudType::Ptr c(new PointCloudType);
  c->height=HEIGHT;
  c->width=WIDTH;
  const unsigned int size( c->height*c->width);
  c->points.resize(size);
  for(unsigned int i(0);i<size;++i){
    c->points[i].x = 1024 * rand () / (RAND_MAX + 1.0f);
    c->points[i].y = 1024 * rand () / (RAND_MAX + 1.0f);
    c->points[i].z = 1024 * rand () / (RAND_MAX + 1.0f);
  }
  std::cout << "Filled cloud height: " << c->height << " ** widht = "
            << c->width << " ** size: " << c->points.size()
            << "\n"
  ;
  boost::thread vis_thread( boost::bind( &Visualize, boost::ref(c) ) );
  char exit;
  std::vector<PointType> new_points;
  new_points.resize(10);
  PointType new_point;

  while(exit!='q') {
    for(unsigned int i(0);i<10;++i) {
      new_point.x = 2000 * rand () / (RAND_MAX + 1.0f);
      new_point.y = 2000 * rand () / (RAND_MAX + 1.0f);
      new_point.z = 2000 * rand () / (RAND_MAX + 1.0f);
      std::cout << "New point " << i << " with x = " << new_point.x
                << " ; y = " << new_point.y << " ; z = "
                << new_point.z << "\n"
      ;
      new_points.push_back(new_point);
    }
    {
//      boost::lock_guard<MutexType> lock(safe_update);
      MutexType::scoped_lock lock(safe_update);
      c->insert( c->points.end(), new_points.begin(), new_points.end() );
      has_to_update = true;
    } // end scoped_lock
    std::cout << "Exit?: ";
    std::cin>>exit;
  }
  vis_thread.join();
  return 0;
}

感谢您的宝贵时间!

编辑:由于 Windows 无法识别可执行格式,我无法使用调试器(?)我在 Visualize 函数上放置了一些 qDebug() 行(另外,直接调用viewer-&gt;wasStopped() 现在我使用的是可变中间变量,stopped):

void Visualize(PointCloudType::Ptr cloud) {
  pcl::visualization::PCLVisualizer* viewer = new pcl::visualization::PCLVisualizer("Vis in thread",true);
  viewer->setBackgroundColor(1.0,0.0,0.0);
  viewer->addPointCloud<PointType>(cloud, "sample cloud");
  viewer->addCoordinateSystem(1.0);
  viewer->initCameraParameters();
  viewer->resetCamera();

  volatile bool stopped( false );
  int iterations( -1 );

  while(!stopped) {
    ++iterations;
    qDebug() << "Before spinOnce - it: << iteration << "\n";
    viewer->spinOnce(100);
    {
//      boost::lock_guard<MutexType> lock(safe_update);
        MutexType::scoped_lock lock(safe_update);
      if(has_to_update) {
        if(!viewer->updatePointCloud<PointType>(cloud, "sample cloud")) {
          viewer->addPointCloud<PointType>(cloud, "sample cloud");
          viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "sample cloud");
          viewer->resetCamera();
        }
        has_to_update = false;
      }
    } // end scoped_lock
    stopped = viewer->wasStopped();
    qDebug() << "Before a new loop - it:" << iteration << "\n";
  }
  delete viewer;
};

嗯,Before spinOnce 只显示一次,iteration=0Before a new loop从不打印

另一方面,主线程不断计算这些点并将其打印到标准输出,直到输入'q'

似乎可视化线程在viewer-&gt;spinOnce(100) 调用中冻结。如果我使用另一种可视化方法 spin() 而不是 spinOnce(100),则没有任何变化。

也许我的代码中存在数据竞赛,但我一直在检查它,我自己找不到竞赛。

注意:根据 PCL 库文档,spinOnce(int time)调用交互器并更新屏幕一次,而spin()调用交互器并运行一个内部循环

编辑 #2: 今天我尝试在 Ubuntu 中再次执行代码,结果导致 PCL 可视化器出现死锁。我添加了一些volatile 关键字和一个新的循环检查。现在看来一切顺利(至少它按预期工作,没有错误的转弯......)。这是新版本:

全局变量:

volatile bool has_to_update(true); // as suggested by @daramarak
volatile bool quit(false);         // new while loop control var

可视化方法:

void Visualize(PointCloudType::Ptr cloud) {
  pcl::visualization::PCLVisualizer* viewer = new pcl::visualization::PCLVisualizer("Vis in thread",true);
  viewer->setBackgroundColor(1.0,0.0,0.0);
  viewer->addPointCloud<PointType>(cloud, "sample cloud");
  viewer->addCoordinateSystem(1.0);
  viewer->initCameraParameters();
  viewer->resetCamera();

  while(!viewer->wasStopped() && !quit ) {
    viewer->spinOnce(100);
    {
      MutexType::scoped_lock lock(safe_update);
      if(has_to_update) {
        if(!viewer->updatePointCloud<PointType>(cloud, "sample cloud")) {
          viewer->addPointCloud<PointType>(cloud, "sample cloud");
          viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "sample cloud");
          viewer->resetCamera();
        }
        has_to_update = false;
      }
    } // end scoped_lock
  }
  delete viewer;
};

主要功能:

// everything the same until...
std::cin>>exit;
quit = (exit=='q');
// no more changes

但是,我不喜欢新的控制循环 var hack。有没有更好的方法来知道何时退出?现在,我无法实现任何其他方式......

【问题讨论】:

  • 在我看来,PCL 库在 Windows 上的另一个线程中呈现时存在问题。顺便说一句,has_to_update 也应该是 volatile 的。
  • 是的,多线程渲染有问题。文档中有一条注释:This class can NOT be used across multiple threads. Only call functions of objects of this class from the same thread that they were created in! Some methods, e.g. addPointCloud, will crash if called from other threads.。但是,在这篇文章中,作者使它起作用了:PCLVisualizer class。在 Ubuntu 中,它起作用了。我将尝试绕过 PCL 可视化库并直接使用 VTK。我会及时通知你进展:)
  • @daramarak。我在问题中添加了一个新的“编辑”。似乎在 Ubuntu 中 spinOnce 有时也会死锁,只是它不像在 Windows 中那样频繁。现在看来,我已经设法避免了这种情况。也就是说,Windows 中的新代码死锁,就好像没有进行任何更改一样。

标签: c++ windows-7 boost-thread point-cloud-library


【解决方案1】:

我相信 wasStopped() 函数是一个 const 成员函数,因此不会改变对象的状态,所以这里可能有一个优化(它可能会缓存 wasStopped() 值,因为编译器会假设答案获胜'不会改变。我建议你尝试将查看器包装在另一个带有函数 bool wasStopped() volatile 的对象中,这可能会阻止此类优化。

【讨论】:

  • 感谢您的回复。我会试试你的建议。只是一个问题,为什么这种优化会“阻止”Windows 中的线程而不是 Ubuntu 中的线程?。
  • 我猜只有当你在那些平台上使用不同的编译器/版本时才会发生这种情况。您当然也可以尝试使用调试器并尝试以这种方式确认我的怀疑。如果编译时间很长,它可能是一个不错的选择。
  • 很遗憾,这里没有调试器选项。我尝试了几次使用调试符号编译应用程序,但 QtCreator 似乎忽略了我。 Windows 终端中的 g++.exe 也是如此。
  • 是的。我认为问题出在 Windows 本身。每当我尝试运行调试器时,都会弹出此消息:不是可执行格式:文件格式无法识别。奇怪,因为安装了调试符号和调试器。我讨厌 Windows。
  • 嗨。我将查看器包装在一个类中,并将wasStopped 重新定义为:bool wasStopped() volatile { return v-&gt;wasStopped(); }。它没有用。然后,我没有使用pcl::visualization::PCLViewer* viewer,而是将其用作PCLViewer viewer。然后,一个新的错误出现:error: passing 'volatile pcl::visualization::PCLVisualizer' as 'this' argument of 'bool pcl::visualization::PCLVisualizer::wasStopped() const' discards qualifiers
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多