QSharedPointer或std :: shared_ptr的生命周期

时间:2017-02-01 17:09:35

标签: c++ multithreading qt shared-ptr qsharedpointer

在我的申请中

我有MainWindowQtMainWindow班级)和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);
    }
}

在我的MainWindow中我已经

// 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,那会附加什么。

在相关问题中:Waiting slots to be executed before quitting

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

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

由于

2 个答案:

答案 0 :(得分:1)

  

我的QSharedPointer是否始终有效?

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

  

如果在处理(MainWindow)期间发生usb_read(),会附加什么   并且memcpy写在我的图像上。

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

  

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

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

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

答案 1 :(得分:1)

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

在您的情况下,您将只有一次共享指针的实例,这是您在Acquisition线程开始时创建的实例。它在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中的不同线程之间调用emit信号时,默认情况下将使用排队连接。这意味着在发送线程上,应该调用的数据和处理程序将被放入队列中。这里的数据是您的共享指针。即使获取线程完成,队列也会保持活动状态。然后当另一个线程启动(MainThread等)时,数据将被出列,并且将使用它调用信号处理程序。