简体   繁体   English

QSharedPointer或std :: shared_ptr的生命周期

[英]life cycle of QSharedPointer or std::shared_ptr

in my application 在我的申请中

I've a MainWindow (which is a QtMainWindow class) and a Acquisiton class (which is a QThread class) 我有一个MainWindow (它是QtMainWindow类)和Acquisiton类(这是QThread类)

Here my very simplified 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);
    }
}

and in my MainWindow I've 在我的MainWindow中

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

I don't understand the lifecycle of the QSharedPointer (and std::shared_ptr (imagine the samecode with std::shared_ptr) 我不了解QSharedPointer(和std :: shared_ptr的生命周期)(想象与std :: shared_ptr相同的代码)

Does my QSharedPointer is always valid ? 我的QSharedPointer是否一直有效? What append if during processing (MainWindow), the usb_read() occurs and the memcpy write on my image. 如果在处理(MainWindow)期间出现usb_read()并将memcpy写在我的图像上,该追加什么内容。

In a related question: Waiting slots to be executed before quitting 在一个相关的问题中: 退出前要执行等待的插槽

I see that QSharedPointer keeps my data valid if the acquisition threads stop during data is processing. 我看到,如果在数据处理期间停止了获取线程,则QSharedPointer会使我的数据保持有效。

In this case, is my signal canceled, my values are copied somewhere or the thread wait for my MainWindow to finish processing ? 在这种情况下,是否取消了我的信号,将我的值复制到了某个地方,还是线程等待我的MainWindow完成处理?

Thanks 谢谢

Does my QSharedPointer is always valid? 我的QSharedPointer是否一直有效?

Only after you copy the data to it but after that yes, it will be valid as long as any instance of it exists so as long as your object of type Acquisition exists. 仅在将数据复制到数据之后,然后再复制,只要它的任何实例存在,只要您的类型为Acquisition的对象存在,它就将是有效的。

What append if during processing (MainWindow), the usb_read() occurs and the memcpy write on my image. 如果在处理(MainWindow)期间出现usb_read()并将memcpy写在我的图像上,该追加什么内容。

Race condition. 比赛条件。 You would have to use a mutex to lock the resource when processing in the MainWindow . MainWindow处理时,必须使用互斥锁来锁定资源。 Smart pointers are not inherently thread safe however QSharedPointer uses atomic integer for reference counting so sharing is thread safe. 智能指针本质上不是线程安全的,但是QSharedPointer使用原子整数进行引用计数,因此共享是线程安全的。 Again, the content is not! 再次,内容不是!

In this case, is my signal canceled, my values are copied somewhere or the thread wait for my MainWindow to finish processing ? 在这种情况下,是否取消了我的信号,将我的值复制到了某个地方,还是线程等待我的MainWindow完成处理?

This depends on how you connect your objects. 这取决于您如何连接对象。 By default when two QObjects live in two different threads the connection is automatically Qt::QueuedConnection and in that case the arguments are first copied (even if sent as const reference) internally to be posted as event in the receiver's thread. 默认情况下,当两个QObjects位于两个不同的线程中时,连接将自动为Qt::QueuedConnection ,在这种情况下,将首先在内部复制参数(即使作为const引用发送),然后将其作为事件发布到接收者的线程中。 This requires the argument to be copyable and the receiver's thread to be running an event loop. 这要求参数是可复制的,并且接收者的线程正在运行事件循环。 However if you for some reason do Qt::DirectConnection which is default for connection in the same thread it will be equivalent to direct call. 但是,如果由于某种原因执行Qt::DirectConnection这是默认情况下在同一线程中进行连接),则等效于直接调用。 This may happen in your case if you have connected the two objects before you moved one of them to a different thread (however maybe Qt does switch all connections to queued ones when QObject::moveToThread is called). 如果您在将两个对象中的一个移到另一个线程之前已经连接了两个对象,则可能会发生这种情况(但是,当调用QObject::moveToThread时,Qt可能QObject::moveToThread所有连接都切换到排队的QObject::moveToThread )。

So to answer directly, when queued signal is used the arguments are copied and life time of the caller does no longer matter after the emit . 因此,直接回答是,当使用排队信号时,将复制参数,并且emit之后,调用方的寿命不再重要。

As it was already written in Resurrection's answer shared pointers are valid as long as they are at least referenced at one location. 正如它已经写在复活的答案中一样,共享指针只要至少在一个位置被引用就有效。

In your case you will only have once instance of the shared pointer, which is the one you create at the start of the Acquisition thread. 在您的情况下,共享指针将只有一个实例,这是您在Acquisition线程开始时创建的实例。 It is referenced in the Acquisition thread as well as in the signal handlers that will be called by QT. 它在Acquisition线程以及将由QT调用的信号处理程序中引用。 As you have only one shared pointer (with one byte array in it) you are now updating the same data buffer on each acquisition and overwrite it, potentially at the same moment when another thread has not yet read it. 由于只有一个共享指针(其中有一个字节数组),因此现在每次获取时都更新相同的数据缓冲区并覆盖它,可能是在另一个线程尚未读取它的同时。 You can however easily fix that by creating a new shared pointer instance for each sample and pass that one to the other thread in the signal. 但是,您可以通过为每个样本创建一个新的共享指针实例并将其传递给信号中的另一个线程来轻松解决此问题。

The following small change should do it: 可以进行以下小的更改:

//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);
    }
}

And regarding the cancellation and signaling: When you call emit signals between different threads in QT then by default a queued connection will be used. 关于取消和信令:当您调用QT中不同线程之间的发射信号时,默认情况下将使用排队连接。 This means on the emitting thread the data and the handler that should be called will be put in a queue. 这意味着在发出线程上,应将数据和应调用的处理程序放入队列中。 The data here is your shared pointer. 此处的数据是您的共享指针。 The queue will held it alive, even if the acquisition thread finishes. 即使获取线程完成,该队列也将保持活动状态。 Then when the other thread kicks in (MainThread, etc.) the data will be dequeued and the signal handler will be called with it. 然后,当另一个线程踢入(MainThread等)时,数据将出队,并随之调用信号处理程序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM