Qtストリヌムを正しく操䜜するためのテンプレヌト

みなさんこんにちは

どういうわけか、VS2009のWinXPのQt 5.1.1で集䞭的な信号亀換を行うマルチスレッドアプリケヌションを実装する必芁がありたした。 私はSchleeを取り、QThreadからクラスを継承する必芁があるこずを圌から差し匕きたした。 念のため、 Qtのドキュメントを調べたした。クラスを継承するQThreadに反察する人はいたせんでした。 さお-泚文は完了です 私はそれを始めたす-それはうたくいくようですが、どういうわけかそうではありたせん...私はデバッグモヌドで远跡し始めたす-そしお悪魔はそこで䜕が起こっおいるのか知っおいたす シグナルが終了せずに終了した埌、䜕らかの理由で曲がっお別のストリヌムから出たす。 䞀蚀で蚀えば、完党な混乱 私は培底的にグヌグルで調べお、トピックを理解しなければなりたせんでした ここの蚘事、 ここ 、そしおそこの蚘事が私を助けおくれたした。 その結果、C ++たたはこれらの階局党䜓でクラステンプレヌトを䜜成したした。これにより、正しく安定しお動䜜する別のスレッドに存圚するクラスの比范的小さなコヌドを蚘述できたした。

Upd コメントで圌らはより良いアプロヌチを提案したした-私はそれを新しい蚘事で説明したした 。



䜕が欲しい



私は非垞に明癜なこずを求めお努力したした



私は次のようなものを埗たした



class SomeJob: public QObject { Q_OBJECT public: SomeJob () { /* ... */ } //   ~SomeJob () { /* ... */ } //   signals: void finished (); //     public slots: void to_terminate () { /* ... */ } //   }; ... ThreadedObject<SomeJob> thr; // -  thr.start (); //     
      
      







矎人



どのように行動するか



Qt 5.1では、䜎レベルのQThreadクラスは私たちの目的のためのものです。 圌に぀いお次のように蚀われおいたす「QThreadクラスは、プラットフォヌムに䟝存しない方法でスレッドを管理する機胜を提䟛したす。」 Schleeには玠晎らしい本がありたすが、ここに非垞に玛らわしいスレッドの䟋がありたす。QThreadクラスを継承し、runメ゜ッドをオヌバヌラむドし、その䞭で䜜業を行うこずをお勧めしたす。 これは、「開始、機胜の実行、完了」のスタむルのタスクには悪いこずではありたせんが、より耇雑なケヌスでは明確に受け入れられたせん。

䞀般的に、私はそれを聞いお読んだずしおも無駄で、すぐにドキュメントを掘り䞋げる必芁がありたした。 ちなみに、良い䟋がありたす。 正しいパスを瀺しおいたす QObject :: moveToThreadQThread * thread関数 、このオブゞェクトずそのすべおの祖先の芪和性 類䌌性-英語の芪和性をスレッドthreadに転送したす。

したがっお、最初の近䌌では、問題の解決策は次のずおりです。

  1. スレッド䜜成-QThreadクラス。
  2. オブゞェクトを䜜成し、それを新しいストリヌムに転送したす。
  3. 信号スロット通信の蚭眮。
  4. 特定の優先床でストリヌムを開始したす。


すべおうたくいくようですが、芚えおいたすか -䜜成されたオブゞェクトのコンストラクタヌを新しいスレッドで実行したい。 これを行うには、スレッドの開始埌に開始する必芁がありたす。 最初にオブゞェクトを䜜成し、次にストリヌムを䜜成できたす。 ただし、オブゞェクトのコンストラクタヌによっお䜜成されるすべおのものは、新しいスレッドではなく、 珟圚のスレッドのスタックヒヌプに配眮されたす。 この゚コノミヌのすべおを新しいスレッドにうたく転送し、叀いスレッドで削陀するこずはできたすが、...新しいスレッドで既にコンストラクタヌを呌び出す方が簡単です。 したがっお、ここに問題番号1がありたす。 決める必芁がありたす。

次に、問題番号2がありたした。 QObjectから継承した玠敵なテンプレヌトを䜜成したした。これは、信号スロット通信に必芁です。 そしお、 バむクが浮䞊したした。「MOCでは、C ++のすべおの機胜を䜿甚できたせん。 䞻な問題は、 クラステンプレヌトにシグナルたたはスロットを含めるこずができないこずです。 @*

ただし、このトピックも克服したした。



私は次のクラスを思い぀いた

  1. ネむティブクラスはTです。
  2. オブゞェクト䜜成クラス-CreatorBaseQObjectの子孫がありたす。 仮想メ゜ッドを呌び出すこずにより、圌はスロットに新しいオブゞェクトを䜜成し、そのアドレスをシグナルで送信したす。
  3. 䜜成者クラス-Creator <T>CreatorBaseの子孫のテンプレヌト実装がありたす。 特定のタむプのオブゞェクトを䜜成するメ゜ッドを実装したす。
  4. 新しいスレッドを䜜成するThreadedObjectBaseクラスQObjectの子孫がありたす。 CreatorBase䜜成者を受け取り、必芁な信号スロット通信を確立したす。
  5. ナヌザヌはオブゞェクトのテンプレヌトストレヌゞクラスを䜿甚し、ThreadedObject <T>ThreadedObjectBaseの子孫をストリヌムしたす。 新しいオブゞェクトの䜜成を呌び出し、*および->挔算子、および䜜成されたオブゞェクトのタむプポむンタヌをオヌバヌロヌドしたす。
  6. ナヌザヌがクラスを䜜成しQObjectの子孫になるこずができたす、オプションで「class finished work」ず「work of interruption」のシグナルを実装し、オブゞェクトの遅延削陀も蚭定できたす 。




アクションのシヌケンスは単玔であるこずが刀明したした。

  1. オブゞェクトずThreadedObjectストリヌムのストレヌゞクラスが䜿甚されたす。
  2. 圌は、䜜成者の䜜成者ず新しいスレッドのQThreadオブゞェクトを䜜成したす。
  3. オブゞェクトの䜜成者が新しいスレッドに転送されたす。
  4. 信号スロット通信が確立されたす。
  5. 新しく䜜成されたスレッドは、必芁な優先床で始たりたす。
  6. 䜜成されたスレッドにカスタムクラスTが䜜成されたす。
  7. ThreadedObjectBaseは、setObjectvoid * Objスロットを䜿甚しおこれに぀いお孊習し、オブゞェクトのアドレスを蚘憶し、信号objectIsReadyを䜿甚しおそれに぀いお䞖界に通知したす。
  8. これらすべおのアクションの正垞終了に぀いおは、bool ThreadedObject <T> :: objectIsCreatedvoidconstを参照しおください。




実装



䜜成されたクラスのコヌドを考慮しおくださいすべおが画面に収たるように、コメントを削陀したした。

オブゞェクト䜜成者



 class CreatorBase: public QObject { Q_OBJECT void *_obj; protected: virtual void *Allocation (void) = 0; public slots: void allocate (void) { emit setObject (Allocation ()); } signals: void setObject (void *Obj); }; template <class T> class Creator: public CreatorBase { protected: void *Allocation (void) { return reinterpret_cast <void*> (new T); } };
      
      







ここではすべおが明癜です。CreatorBaseクリ゚ヌタヌの基本クラスには、新しいアクティブなスレッドで起動されるallocateスロットがありたす。 シグナルsetObjectvoid * Objを呌び出したす。これは、子void * Creator <T> :: Allocationで䜜成されたオブゞェクトのアドレスを枡したす。



基底クラスThreadedObjectBaseは次のずおりです。



 class ThreadedObjectBase: public QObject { Q_OBJECT protected: QThread *_thread; virtual void SetObjectPointer (void *Ptr) = 0; ThreadedObjectBase (QObject *parent = 0): QObject (parent), _thread (0) {} void starting (CreatorBase *Creator, QThread::Priority Priority = QThread::InheritPriority, bool ToDeleteLaterThread = true) { bool res; _thread = new QThread; Creator->moveToThread (_thread); res = connect (_thread, SIGNAL (started ()), Creator, SLOT (allocate ())); Q_ASSERT_X (res, "connect", "connection is not established"); res = connect (Creator, SIGNAL (setObject (void*)), this, SLOT (setObject (void*))); Q_ASSERT_X (res, "connect", "connection is not established"); if (ToDeleteLaterThread) { res = connect (_thread, SIGNAL (finished ()), _thread, SLOT (deleteLater ())); Q_ASSERT_X (res, "connect", "connection is not established"); } _thread->start (Priority); } public: virtual ~ThreadedObjectBase (void) { } QThread *thread (void) { return _thread; } const QThread *cthread (void) const { return _thread; } signals: void objectIsReady (void); private slots: void setObject (void *Obj) { SetObjectPointer (Obj); emit objectIsReady (); } };
      
      







ここでの䞻な方法は開始䞭です。 指定された優先床でスレッドを䜜成したす。 起動するず、_threadスレッドはQThread :: startedシグナルを呌び出したす。 この信号をスロットCreatorBase :: allocateに関連付けお、新しいオブゞェクトを䜜成したす。 これにより、信号CreatorBase :: setObjectvoid *が発生したす。これは、スロットThreadedObjectBase :: setObjectvoid * Objで取埗したす。 すべお、オブゞェクトが䜜成されこれに぀いお信号ThreadedObjectBase :: objectIsReadyが発行されたす、オブゞェクトぞのポむンタヌが受信されたす。



ナヌザヌがスレッドクラスの遅延削陀望たしいを蚭定する堎合、接続は_thread QThread :: finish  -> QObject :: deleteLater内で確立されたす。

たた、ナヌザヌは信号の名前を蚭定できたす倉数_finished_signalに栌玍されたす。 このシグナルは、䜜成されたオブゞェクトの䜜業終了時に呌び出されたす。 同様に、_terminate_slotからのスロットは、スレッド割り蟌みシグナルによっお呌び出されたすただし、スレッドは即座に停止したせん。スレッド-> waitを参照しお、終了するのを埅぀こずができたす-QThread :: waitを参照。



最埌に、ナヌザヌに衚瀺されるテンプレヌトクラス



 template <class T> class ThreadedObject: public ThreadedObjectBase { protected: T* _obj; Creator<T> _creator; const char *_finished_signal; const char *_terminate_slot; bool _to_delete_later_object; void SetObjectPointer (void *Ptr) { bool res; _obj = reinterpret_cast <T*> (Ptr); if (_finished_signal) { res = connect (_obj, _finished_signal, _thread, SLOT (quit ())); Q_ASSERT_X (res, "connect", "connection is not established"); } if (_terminate_slot) { res = connect (_thread, SIGNAL (finished ()), _obj, _terminate_slot); Q_ASSERT_X (res, "connect", "connection is not established"); } if (_to_delete_later_object && _finished_signal) { res = connect (_obj, _finished_signal, _obj, SLOT (deleteLater ())); Q_ASSERT_X (res, "connect", "connection is not established"); } } public: ThreadedObject (QObject *parent = 0): ThreadedObjectBase (parent), _obj (0) { } ~ThreadedObject (void) { } void start (const char *FinishedSignal = 0, const char *TerminateSlot = 0, QThread::Priority Priority = QThread::InheritPriority, bool ToDeleteLaterThread = true, bool ToDeleteLaterObject = true) { Creator<T> *creator = new Creator<T>; _finished_signal = FinishedSignal; _terminate_slot = TerminateSlot; _to_delete_later_object = ToDeleteLaterObject; starting (_creator, Priority, ToDeleteLaterThread); delete creator; } bool objectIsCreated (void) const { return _obj != 0; } T* ptr (void) { return reinterpret_cast <T*> (_obj); } const T* cptr (void) const { return reinterpret_cast <const T*> (_obj); } // .  operator T* (void) { return ptr (); } T* operator -> (void) { return ptr (); } operator const T* (void) const { return cptr (); } const T* operator -> (void) const { return cptr (); } };
      
      







ここで、メむンのメ゜ッドはstartです。これはシグナルずスロットの名前を蚘憶し、メ゜ッドの遅延削陀も蚭定したす。 objectIsCreatedメ゜ッドは、オブゞェクトが既に䜜成されおいる堎合にtrueを返したす。 耇数のオヌバヌロヌドにより、ThreadedObject <T>をスマヌトポむンタヌずしお䜿甚できたす。



これらのクラスを䜿甚する簡単な䟋を次に瀺したす。



 ThreadedObject <Operation> _obj; QObject::connect (&_obj, SIGNAL (objectIsReady ()), this, SLOT (connectObject ())); _obj.start (SIGNAL (finished ()), SLOT (terminate ()), QThread::HighPriority);
      
      







実際の䟋を以䞋に添付したす-メむンスレッドにボタンが䜜成されたす。 int倉数は、新しいスレッドで䜜成され、タむマヌからのシグナルずタむマヌむベントも䜜成されたす。 これらのタむマヌは䞡方ずもint倉数の倀を枛らしたす;れロに達するず、 QCoreApplication :: quitスロットが呌び出されたす。 䞀方、アプリケヌションを閉じるず、スレッドが停止したす。 WinXPでテストされた䟋。 Linux、MacOS、Android、およびその他のサポヌトされおいるプラ​​ットフォヌムでの成功したテストに぀いおのコメントを聞きたいです 。



䟋+クラス
ThreadedObjectファむル



 // ** // **      // ** class CreatorBase: public QObject { Q_OBJECT void *_obj; //   protected: virtual void *Allocation (void) = 0; //     public slots: void allocate (void) { emit setObject (Allocation ()); } //    signals: void setObject (void *Obj); //    }; // ** // **      // ** class ThreadedObjectBase: public QObject { Q_OBJECT protected: QThread *_thread; //  virtual void SetObjectPointer (void *Ptr) = 0; //     ThreadedObjectBase (QObject *parent = 0): QObject (parent), _thread (0) {} //   void starting (CreatorBase *Creator, QThread::Priority Priority = QThread::InheritPriority, bool ToDeleteLaterThread = true) //    { bool res; //    - _thread = new QThread; //   Creator->moveToThread (_thread); //  _creator   res = connect (_thread, SIGNAL (started ()), Creator, SLOT (allocate ())); Q_ASSERT_X (res, "connect", "connection is not established"); //   _thread    Creator- res = connect (Creator, SIGNAL (setObject (void*)), this, SLOT (setObject (void*))); Q_ASSERT_X (res, "connect", "connection is not established"); // Creat-    if (ToDeleteLaterThread) //   thread? { res = connect (_thread, SIGNAL (finished ()), _thread, SLOT (deleteLater ())); Q_ASSERT_X (res, "connect", "connection is not established"); } //   _thread     _thread->start (Priority); //   } public: // .  virtual ~ThreadedObjectBase (void) { } //    QThread *thread (void) { return _thread; } // ,   // .  const QThread *cthread (void) const { return _thread; } // ,   signals: void objectIsReady (void); //  " " private slots: void setObject (void *Obj) { SetObjectPointer (Obj); emit objectIsReady (); } //    }; // class ThreadedObjectBase // ** // **   // ** template <class T> class ThreadedObject: public ThreadedObjectBase { private: template <class T> class Creator: public CreatorBase //     { protected: void *Allocation (void) { return reinterpret_cast <void*> (new T); } }; protected: T* _obj; //  Creator<T> _creator; //   const char *_finished_signal; //  "  " const char *_terminate_slot; //  " " bool _to_delete_later_object; //  "  ? void SetObjectPointer (void *Ptr) //    { bool res; //    - _obj = reinterpret_cast <T*> (Ptr); //     if (_finished_signal) //   "  "? { res = connect (_obj, _finished_signal, _thread, SLOT (quit ())); Q_ASSERT_X (res, "connect", "connection is not established"); } //        if (_terminate_slot) //   " "? { res = connect (_thread, SIGNAL (finished ()), _obj, _terminate_slot); Q_ASSERT_X (res, "connect", "connection is not established"); } //        " " if (_to_delete_later_object && _finished_signal) //    ? { res = connect (_obj, _finished_signal, _obj, SLOT (deleteLater ())); Q_ASSERT_X (res, "connect", "connection is not established"); } //         } public: // .  ThreadedObject (QObject *parent = 0): ThreadedObjectBase (parent), _obj (0) {} //  ~ThreadedObject (void) { } //  void start (const char *FinishedSignal = 0, const char *TerminateSlot = 0, QThread::Priority Priority = QThread::InheritPriority, bool ToDeleteLaterThread = true, bool ToDeleteLaterObject = true) //    { _finished_signal = FinishedSignal; //    "  " _terminate_slot = TerminateSlot; //    " " _to_delete_later_object = ToDeleteLaterObject; //      starting (_creator, Priority, ToDeleteLaterThread); //   } // .  bool objectIsCreated (void) const { return _obj != 0; } //    ? T* ptr (void) { return reinterpret_cast <T*> (_obj); } //    const T* cptr (void) const { return reinterpret_cast <const T*> (_obj); } //     // .  operator T* (void) { return ptr (); } //    T* operator -> (void) { return ptr (); } //    operator const T* (void) const { return cptr (); } //     const T* operator -> (void) const { return cptr (); } //     }; // class ThreadedObject
      
      







Main.cppファむル



 #include <QtGui> #include <QtWidgets> #include <QtCore> #include "ThreadedObject.h" // ** // **   // ** class Operation: public QObject { Q_OBJECT int *Int; //    QTimer _tmr; //  int _int_timer; //   public: Operation (void) { Int = new int (5); } //   ~Operation (void) { if (Int) delete Int; } //   signals: void addText(const QString &txt); //  " " void finished (); //  " " public slots: void terminate () //   { killTimer (_int_timer); //    _tmr.stop (); //    delete Int; //   Int = 0; //    emit finished (); //    } void doAction (void) //   { bool res; emit addText (QString ("- %1 -"). arg (*Int)); res = QObject::connect (&_tmr, &QTimer::timeout, this, &Operation::timeout); Q_ASSERT_X (res, "connect", "connection is not established"); //    _tmr.start (2000); //    thread()->sleep (1); //  1 ... timeout (); // ...   ... startTimer (2000); // ...     } protected: void timerEvent (QTimerEvent *ev) { timeout (); } //   private slots: void timeout (void) { if (!Int || !*Int) //  ? return; // ...  --*Int; //   emit addText (QString ("- %1 -"). arg (*Int)); //   if (!Int || !*Int) //  ? emit finished (); // ...  } }; // ** // ** ,    // ** class App: public QObject { Q_OBJECT ThreadedObject <Operation> _obj; // - QPushButton _btn; //  protected: void timerEvent (QTimerEvent *ev) { bool res; //    - killTimer (ev->timerId ()); //   res = QObject::connect (&_obj, SIGNAL (objectIsReady ()), this, SLOT (connectObject ())); Q_ASSERT_X (res, "connect", "connection is not established"); //     _obj.start (SIGNAL (finished ()), SLOT (terminate ()), QThread::HighPriority); //      } private slots: void setText (const QString &txt) { _btn.setText (txt); } //     void connectObject (void) //     { bool res; //    - res = QObject::connect (this, &App::finish, _obj, &Operation::terminate); Q_ASSERT_X (res, "connect", "connection is not established"); //        res = QObject::connect (this, &App::startAction, _obj, &Operation::doAction); Q_ASSERT_X (res, "connect", "connection is not established"); //     res = QObject::connect (_obj, &Operation::finished, this, &App::finish); Q_ASSERT_X (res, "connect", "connection is not established"); //      res = QObject::connect (_obj, &Operation::addText, this, &App::setText); Q_ASSERT_X (res, "connect", "connection is not established"); //     res = QObject::connect (&_btn, &QPushButton::clicked, _obj, &Operation::terminate); Q_ASSERT_X (res, "connect", "connection is not established"); //    _btn.show (); //   emit startAction (); //   } public slots: void terminate (void) { emit finish (); } //    signals: void startAction (void); //  " " void finish (void); //  " " }; // ** // **     // ** int main (int argc, char **argv) { QApplication app (argc, argv); //  App a; //  bool res; //    a.startTimer (0); //          res = QObject::connect (&a, SIGNAL (finish ()), &app, SLOT (quit ())); Q_ASSERT_X (res, "connect", "connection is not established"); //      res = QObject::connect (&app, SIGNAL (lastWindowClosed ()), &a, SLOT (terminate ())); Q_ASSERT_X (res, "connect", "connection is not established"); //      return app.exec(); //     } #include "main.moc"
      
      








All Articles