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.