最近几周,我读了很多关于RAII的文章,认为我应该开始在我的应用程序中使用智能指针。例如,我尝试修改我的一个应用程序。它从一个线程中的摄像头捕获帧,在另一个线程中执行图像处理,并在QT小部件中显示已处理和未处理的图像。一个中心对象是CCameraHandler,它控制捕获线程和图像处理线程。到目前为止,我在这个类中使用了4个普通指针作为成员:
CCameraCapture* m_CameraCapture;
CImageProcessor* m_ImageProcessor;
QThread* m_CameraCaptureThread;
QThread* m_ProcessingThread;在CCameraHandler的构造函数中,我使用new创建了实例,并将捕获对象移动到线程中:
m_CameraCaptureThread= new QThread();
m_CameraCapture= new CCameraCapture();
//Move camera capture object to thread
m_CameraCapture->moveToThread(m_CameraCaptureThread);这种方法效果很好。现在,我想使用QScopedPointer进行第一次测试,并尝试使用以下命令将m_CameraCapture更改为QScopedPointer
QScopedPointer<CCameraCapture> m_CameraCapture;并使用初始化列表中的新的CameraCapture( CCameraCapture())对其进行初始化。它编译得很好,工作正常,但当我关闭应用程序,调用析构函数时,我从Qt得到一个错误:“无法向另一个线程拥有的对象发送事件。当前线程5ff590. Receiver‘(类型为'CCameraCapture')是在线程4b7780中创建的。”我猜它与我现在移动作用域指针的m_CameraCapture->moveToThread(m_CameraCaptureThread);有关。CCameraCapture会自动将QScopedPointer设置为父对象吗?到目前为止,我使用了
//This connections guarantees that the m_CCameraCapture and m_CameraCapture are deleted after calling QThread::exit()
QObject::connect(m_CameraCaptureThread, SIGNAL(finished()), m_CameraCaptureThread, SLOT(deleteLater()));
QObject::connect(m_CameraCaptureThread, SIGNAL(finished()), m_CameraCapture, SLOT(deleteLater()));停止摄像头采集时删除worker线程。如果m_CameraCapture现在是由CCameraHandler创建的,那么可能会导致问题。目前,我不确定在这种情况下使用SmartPointer是否是一个好主意。你知道是什么原因导致这个销毁错误吗?
编辑: CCameraHandler dtor如下所示(线程应该在worker之前删除):
CCameraHandler::~CCameraHandler(void)
{
//Stop grabbing and processing
emit stopGrabbing();
emit stopProcessing();
/*Wait for the capture thread to terminate. The destructor of CCamera Handler might be called on application close. Therefore it is important to wait for QThreads to terminate. Else the application might close before threads get deleted*/
m_CameraCaptureThread->exit();
m_CameraCaptureThread->wait();
//Wait for the processing thread to terminate
m_ProcessingThread->exit();
m_CameraCaptureThread->wait();
qDebug() << "CCameraHandler deleted";
}发布于 2014-04-09 03:54:38
被移动到另一个线程的对象必须被析构:
从线程本身执行
警告:在停止QThread之前,销毁它是不安全的。要安全地对仅运行事件循环的线程执行此操作,应改用以下子类:
class Thread : public QThread {
using QThread::run; // final
public:
Thread(QObject * parent = 0) : QThread(parent) {}
~Thread() { quit(); wait(); }
};给定这样一个从GUI线程中析构的类,您只需在析构移动到线程中的任何对象之前将其析构即可。当然,根本没有必要将这样的对象保存为指针,但无论您直接将它们保存还是作为指针保存,下面的代码都可以工作。
class Foo : public Bar {
CCameraCapture m_CameraCapture;
CImageProcessor m_ImageProcessor;
Thread m_CameraCaptureThread;
Thread m_ProcessingThread;
...
}当类被析构时,会按顺序发生以下情况:
~Foo()主体运行(它可能是线程部分中的empty).m_ProcessingThread.~Thread运行的相反顺序被析构,然后是超类析构函数(~ ...,最后是~QObject)。移动到该线程的任何对象现在都是threadless.m_CameraCaptureThread.~Thread运行,后面跟着超类析构函数。移动到该线程的任何对象现在都是运行的threadless.m_ImageProcessor析构函数。作为一个无线程对象,销毁是安全的,不受任何thread.m_CameraCapture析构函数的影响。作为无线程对象,销毁对于任何线程都是安全的。如果您使用QScopedPointer<...>保存这些实例,事情将完全相同,只是每个对象的销毁都将被包装在~QScopedPointer<...>的主体中。
请注意,即使使用原始指针来保存这些实例也是过早的悲观:您浪费了一点堆,并且由于额外的间接层,对实例的访问会稍微慢一点。这些孤立的东西可能不会起到很大的作用,但如果有数千个对象都是这样编码的,事情就会变得越来越多。
除非绝对必要,否则不要在单个堆块中分配类成员。
发布于 2014-04-08 20:33:05
问题是,您正在从非UI线程中执行一些UI内容。很难准确地说出问题出在哪里,因为你没有给出CCameraCapture到底做了什么。
我怀疑在捕获帧之后,您在标签上设置了一个像素图(以显示帧),而不是发出一个具有新帧的信号,并将该信号与相应的UI元素插槽连接。所以我认为作用域指针、信号和插槽与您的问题无关,问题是您没有在需要的地方使用信号插槽机制。
https://stackoverflow.com/questions/22931757
复制相似问题