0

在我的应用程序中

我有一个MainWindow(这是一个QtMainWindow类)和一个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 写入我的图像,则附加什么。

在一个相关的问题中:在退出前等待槽执行

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

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

谢谢

4

2 回答 2

1

我的 QSharedPointer 是否始终有效?

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

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

比赛条件。在MainWindow. 智能指针本质上不是线程安全的,但是QSharedPointer使用原子整数进行引用计数,因此共享是线程安全的。再次,内容不是!

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

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

所以直接回答,当使用排队信号时,参数被复制,调用者的生命周期在emit.

于 2017-02-01T17:26:10.653 回答
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 中调用不同线程之间的发出信号时,默认情况下将使用排队连接。这意味着在发射线程上,应该调用的数据和处理程序将被放入队列中。这里的数据是你的共享指针。即使获取线程完成,队列也会保持活动状态。然后,当另一个线程启动(MainThread 等)时,数据将出列,并用它调用信号处理程序。

于 2017-02-01T19:25:56.717 回答