簡體   English   中英

QSharedPointer 在發射內被銷毀

[英]QSharedPointer gets destroyed within emit

我是 Qt 的QSharedPointer ,在QSharedPointer在信號中傳遞時遇到了一些問題。 我正在使用兩個線程(UI 和一個工作線程)。 worker 使用包含自定義 QObject 的 QSharedPointer 的信號向 UI 發送信號:

class MyObject : QObject {...}

class Window : public QWidget {
    Q_OBJECT
public slots:
    void onFound(QSharedPointer<MyObject>);
}

class Worker : public QObject {
    Q_OBJECT
public signals:
    void found(QSharedPointer<MyObject>);
}

我使用Qt::QueuedConnection將通過 windows onFound found的工作人員連接起來,因為他們生活在不同的線程中,因此通信必須是異步的。

現在我觀察以下行為,當我傳遞最后一個QSharedPointer我的對象時:

  • 信號 moc 將指向我的指針的引用轉換為void*並將其歸檔。
  • 函數返回導致共享指針和相應的對象被銷毀。

這不是我所期望的——盡管這是合理的。 QSharedPointer 通常設計為通過這種方式傳遞信號嗎? 如果是這樣,是否有一種機制可以在排隊時保留參考?

我考慮了以下解決方案,但我對它們都不完全滿意:

  1. 在某個地方保留一個引用,以便在排隊時保留引用。 但哪里是一個合理的地方,我應該什么時候放手。
  2. 建立連接Qt::DirectConnection但我仍然必須以某種方式切換線程(與以前相同的情況)
  3. 引入一個帶有std::function參數的新信號/槽,用於傳遞要在目標線程中執行的 lambda 函數並捕獲我的共享指針的副本。 (這是我目前的解決方案,但它並不優雅,不是嗎?)

您還有其他建議或想法嗎?

信號返回不會銷毀相應的對象。 QMetaObject::activate調用復制共享指針。 下面是send信號的實現:

// SIGNAL 0
void IO::send(const QSharedPointer<Unique> & _t1)
{
    void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
    QMetaObject::activate(this, &staticMetaObject, 0, _a);
}

您可能正在經歷一場競賽:當發出信號的線程恢復執行時,目標線程已經收到了該對象。 因此,在發出的線程中,對象已經消失了——因為到那時,它已經消失了。 然而目標對象接收實例就好了。 它工作正常。

下面的示例說明它在單線程和多線程情況下都可以工作,然后通過確保目標線程始終贏得比賽來重現您的問題:

// https://github.com/KubaO/stackoverflown/tree/master/questions/shared-pointer-queued-49133331
#include <QtCore>

class Unique : public QObject {
   Q_OBJECT
   int const m_id = []{
      static QAtomicInteger<int> ctr;
      return ctr.fetchAndAddOrdered(1);
   }();
public:
   int id() const { return m_id; }
};

class IO : public QObject {
   Q_OBJECT
   int m_lastId = -1;
public:
   Q_SIGNAL void send(const QSharedPointer<Unique> &);
   Q_SLOT void receive(const QSharedPointer<Unique> & u) {
      m_lastId = u->id();
   }
   int lastId() const { return m_lastId; }
};

int main(int argc, char ** argv) {
   Q_ASSERT(QT_VERSION >= QT_VERSION_CHECK(5,9,0));
   QCoreApplication app{argc, argv};
   IO src, dst;
   QObject::connect(&src, &IO::send, &dst, &IO::receive, Qt::QueuedConnection);

   QSharedPointer<Unique> u;
   QWeakPointer<Unique> alive;
   int id = -1;

   // Single-threaded case
   alive = (u.reset(new Unique), u);
   id = u->id();
   Q_ASSERT(dst.lastId() != id); // the destination hasn't seen the object yet
   emit src.send(u);
   u.reset();
   Q_ASSERT(!u);                 // we gave up ownership of the object
   Q_ASSERT(dst.lastId() != id); // the destination mustn't seen the object yet
   Q_ASSERT(alive);              // the object must be still alive
   app.processEvents();
   Q_ASSERT(dst.lastId() == id); // the destination must have seen the object now
   Q_ASSERT(!alive);             // the object should have been destroyed by now

   // Multi-threaded setup
   struct Thread : QThread { ~Thread() { quit(); wait(); } } worker;
   worker.start();
   dst.moveToThread(&worker);
   QSemaphore s_src, s_dst;

   // This thread wins the race
   alive = (u.reset(new Unique), u);
   id = u->id();
   Q_ASSERT(dst.lastId() != id);
   QTimer::singleShot(0, &dst, [&]{ s_src.release(); s_dst.acquire(); });
                                 // stop the thread
   s_src.acquire();              // wait for thread to be stopped
   emit src.send(u);
   QTimer::singleShot(0, &dst, [&]{ s_src.release(); });
                                 // resume the main thread when done
   u.reset();
   Q_ASSERT(!u);
   Q_ASSERT(alive);              // we won the race: the object must be still alive
   s_dst.release();              // get the thread running
   s_src.acquire();              // wait for the thread to be done
   Q_ASSERT(dst.lastId() == id);
   Q_ASSERT(!alive);

   // The other thread wins the race
   alive = (u.reset(new Unique), u);
   id = u->id();
   Q_ASSERT(dst.lastId() != id);
   emit src.send(u);
   QTimer::singleShot(0, &dst, [&]{ s_src.release(); });
                                // resume the main thread when done
   u.reset();
   s_src.acquire();             // wait for worker thread to be done
   Q_ASSERT(!u);
   Q_ASSERT(!alive);            // we lost the race: the object must be gone
   Q_ASSERT(dst.lastId() == id); // yet the destination has received it!

   // Ensure the rendezvous logic didn't mess up
   Q_ASSERT(id == 2);
   Q_ASSERT(!s_src.available());
   Q_ASSERT(!s_dst.available());
}

#include "main.moc"

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM