简体   繁体   中英

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)

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

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

Does my QSharedPointer is always valid ? What append if during processing (MainWindow), the usb_read() occurs and the memcpy write on my image.

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.

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

Thanks

Does my QSharedPointer is always valid?

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.

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

Race condition. You would have to use a mutex to lock the resource when processing in the MainWindow . Smart pointers are not inherently thread safe however QSharedPointer uses atomic integer for reference counting so sharing is thread safe. 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 ?

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. 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. 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).

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 .

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. It is referenced in the Acquisition thread as well as in the signal handlers that will be called by 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. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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