QSharedPointer 或 std::shared_ptr 的生命周期

life cycle of QSharedPointer or std::shared_ptr

本文关键字:生命 周期 ptr shared std QSharedPointer      更新时间:2023-10-16

在我的应用程序中

我有一个MainWindow(这是一个QtMainWindow类)和一个Acquisiton类(这是一个QThread类)

在这里,我非常简化的Acquisiton类

//entry point of the thread
void Acquisition::run ()
{
uint8_t*                image_addr;
QSharedPointer<uint8_t> image(new uint8_t[IMG_SIZE]);     
for (;;)
{
if (isInterruptionRequested())
return;

// here, usb_read() give me the adress of a elem in the ring buffer 
img_addr = usb_read(...);
// the ring buffer can possibly be rewritten on the next usb_read() (if bufferlength = 1) so I copy the data into my QSharedPointer
std::memcpy(image.data(), image_addr, sizeof(IMG_SIZE));
// I send this image 
emit imageSent(image);
}
}

在我的主窗口中,我已经

// the slot for the signal imageSent
void newImage(QSharedPointer<uint8_t> image)
{
// process and draw image
}

我不了解QSharedPointer (和 std::shared_ptr 的生命周期(想象一下与 std::shared_ptr 相同的代码)

我的QSharedPointer 总是有效的吗? 如果在处理(主窗口)期间,usb_read()发生并且memcpy写入我的图像,则附加什么。

在相关问题中:在退出之前等待执行插槽

我看到如果采集线程在数据处理期间停止,QSharedPointer 将保持我的数据有效。

在这种情况下,我的信号是否被取消,我的值是否被复制到某处,或者线程等待我的 MainWindow 完成处理?

谢谢

正如在复活的答案中已经写的那样,只要至少在一个位置引用共享指针,它们就有效。

在您的情况下,您将只有一次共享指针的实例,即您在获取线程开始时创建的那个实例。它在采集线程以及将由 QT 调用的信号处理程序中引用。由于您只有一个共享指针(其中包含一个字节数组),因此您现在在每次采集时都会更新相同的数据缓冲区并覆盖它,可能在另一个线程尚未读取它的同一时刻。但是,您可以通过为每个示例创建一个新的共享指针实例并将该实例传递给信号中的另一个线程来轻松解决此问题。

以下小更改应该可以做到这一点:

//entry point of the thread
void Acquisition::run ()
{
uint8_t*                image_addr;
for (;;)
{
if (isInterruptionRequested())
return;

// here, usb_read() give me the adress of a elem in the ring buffer 
img_addr = usb_read(...);
// Create a fresh shared pointer in the scope
QSharedPointer<uint8_t> image(new uint8_t[IMG_SIZE]);   
// the ring buffer can possibly be rewritten on the next usb_read() (if bufferlength = 1) so I copy the data into my QSharedPointer
std::memcpy(image.data(), image_addr, sizeof(IMG_SIZE));
// I send this image 
emit imageSent(image);
}
}

关于取消和信号: 当您在 QT 中的不同线程之间调用发出信号时,默认情况下将使用排队连接。这意味着在发出线程上,应调用的数据和处理程序将被放入队列中。此处的数据是您的共享指针。即使获取线程完成,队列也会使其保持活动状态。然后,当另一个线程启动(MainThread等)时,数据将被取消排队,信号处理程序将被调用。

我的 QSharedPointer 总是有效的吗?

只有在您将数据复制到其中之后,但在那之后是的,只要它的任何实例存在,只要您的类型Acquisition对象存在,它就会有效。

如果在处理(主窗口)期间发生usb_read()发生,则追加什么 和记忆写在我的图像上。

争用条件。在MainWindow中处理时,您必须使用互斥锁来锁定资源。智能指针本质上不是线程安全的,但是QSharedPointer使用原子整数进行引用计数,因此共享是线程安全的。同样,内容不是!

在这种情况下,我的

信号是否被取消,我的值是否被复制到某个地方或 线程等待我的主窗口完成处理?

这取决于您连接对象的方式。默认情况下,当两个QObjects存在于两个不同的线程中时,连接会自动Qt::QueuedConnection在这种情况下,参数首先在内部复制(即使作为 const 引用发送)以作为事件发布到接收方的线程中。这要求参数是可复制的,并且接收方的线程运行事件循环。但是,如果您出于某种原因Qt::DirectConnection这是同一线程中连接的默认方法,则等同于直接调用。如果您在将其中一个对象移动到另一个线程之前连接了这两个对象,则可能会发生这种情况(但是,当调用 Qt 时,Qt 可能会将所有连接切换到排队的连接QObject::moveToThread)。

因此,要直接回答,当使用排队信号时,参数被复制,并且在emit之后调用者的生命周期不再重要。