QSharedPointer 的生命周期或 std::shared_ptr

life cycle of QSharedPointer or std::shared_ptr

在我的申请中

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

这里是我非常简化的 Acquisiton class

//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 总是有效吗? 如果在处理过程中(MainWindow)发生 usb_read() 并且 memcpy 写入我的图像,那么附加什么。

在相关问题中:

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

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

谢谢

Does my QSharedPointer is always valid?

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

What append if during processing (MainWindow), the usb_read() occurs and the memcpy write on my image.

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

In this case, is my signal canceled, my values are copied somewhere or the thread wait for my MainWindow to finish processing ?

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

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

因为它已经写在 Resurrection 的答案中,只要它们至少在一个位置被引用,共享指针就是有效的。

在您的情况下,您将只有一个共享指针实例,即您在获取线程开始时创建的实例。它在 Acquisition 线程以及将由 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 等),数据将出列并调用信号处理程序。