วิธีที่ถูกต้องในการใช้ QThread คืออะไร (โปรดตัวอย่าง)

เอกสารประกอบ Qt สำหรับ QThread บอกว่าให้สร้างคลาสจาก QThread และใช้วิธีการเรียกใช้

ด้านล่างนี้นำมาจากเอกสารประกอบ 4.7 Qthread ...

หากต้องการสร้างเธรดของคุณเอง ให้คลาสย่อย QThread และเรียกใช้ใหม่ () ตัวอย่างเช่น:

 class MyThread : public QThread
 {
 public:
     void run();
 };

 void MyThread::run()
 {
     QTcpSocket socket;
     // connect QTcpSocket's signals somewhere meaningful
     ...
     socket.connectToHost(hostName, portNumber);
     exec();
 }

ดังนั้นในทุก ๆ เธรดที่ฉันสร้างขึ้น ฉันได้ทำสิ่งนั้นแล้วและสำหรับสิ่งส่วนใหญ่มันก็ใช้ได้ดี (ฉันไม่ได้ใช้ MoveToThread(this) ในอ็อบเจ็กต์ใด ๆ ของฉันและมันก็ใช้งานได้ดี)

ฉันประสบปัญหาเมื่อสัปดาห์ที่แล้ว (จัดการเพื่อผ่านมันไปโดยพยายามสร้างวัตถุของฉัน) และพบ ติดตามโพสต์ในบล็อก โดยพื้นฐานแล้วบอกว่าคลาสย่อย QThread ไม่ใช่วิธีที่ถูกต้องในการทำ (และเอกสารประกอบไม่ถูกต้อง)

สิ่งนี้มาจากนักพัฒนา Qt ดังนั้นเมื่อเห็นแวบแรกฉันก็สนใจ และเมื่อไตร่ตรองเพิ่มเติมแล้วก็เห็นด้วยกับเขา ตามหลักการ OO คุณเพียงต้องการคลาสย่อยคลาสเพื่อปรับปรุงคลาสนั้นเพิ่มเติม... ไม่ใช่แค่ใช้วิธีการเรียนโดยตรง... นั่นคือสาเหตุที่คุณยกตัวอย่าง...

สมมติว่าฉันต้องการย้ายคลาส QObject แบบกำหนดเองไปยังเธรด... วิธีที่ 'ถูกต้อง' ในการดำเนินการคืออะไร? ในบล็อกโพสต์นั้น เขา 'บอกว่า' เขามีตัวอย่างอยู่ที่ไหนสักแห่ง... แต่ถ้ามีใครสามารถอธิบายให้ฉันฟังเพิ่มเติมได้ ก็จะได้รับการชื่นชมอย่างมาก!

อัปเดต:

เนื่องจากคำถามนี้ได้รับความสนใจอย่างมาก นี่คือการคัดลอกและวางเอกสารประกอบ 4.8 พร้อมวิธี 'เหมาะสม' ในการใช้งาน QThread

class Worker : public QObject
 {
     Q_OBJECT
     QThread workerThread;

 public slots:
     void doWork(const QString &parameter) {
         // ...
         emit resultReady(result);
     }

 signals:
     void resultReady(const QString &result);
 };

 class Controller : public QObject
 {
     Q_OBJECT
     QThread workerThread;
 public:
     Controller() {
         Worker *worker = new Worker;
         worker->moveToThread(&workerThread);
         connect(workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
         connect(this, SIGNAL(operate(QString)), worker, SLOT(doWork(QString)));
         connect(worker, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString)));
         workerThread.start();
     }
     ~Controller() {
         workerThread.quit();
         workerThread.wait();
     }
 public slots:
     void handleResults(const QString &);
 signals:
     void operate(const QString &);
 };

ฉันยังคงเชื่อว่าคุ้มค่าที่จะชี้ให้เห็นว่าพวกเขารวมสมาชิก Worker::workerThread พิเศษที่ไม่จำเป็นและไม่เคยใช้ในตัวอย่างของพวกเขา ลบชิ้นส่วนนั้นออกและเป็นตัวอย่างที่เหมาะสมของวิธีการทำเกลียวใน Qt


person g19fanatic    schedule 04.11.2010    source แหล่งที่มา
comment
เอกสารแจ้งเพียงว่าไม่แนะนำให้แนะนำสล็อตใหม่ในคลาสย่อย QThread ไม่ได้กล่าวถึงการได้มาจากคลาส QThread มาจาก QThread เป็นไปตามกระบวนทัศน์เดียวกันกับ TThread ของ Delphi/C++ Builder   -  person Zach Saw    schedule 26.11.2013
comment
woboq.com/blog/qthread-you-were- not-doing-so-wrong.html   -  person Zach Saw    schedule 26.11.2013
comment
โค้ดตัวอย่างของพวกเขาจะไม่คอมไพล์จนกว่าคุณจะแก้ไขบรรทัด 'เชื่อมต่อ' แรกให้เป็น ที่อยู่ของ workerThread เช่น: เชื่อมต่อ(&workerThread, SIGNAL(finished()), worker, SLOT(deleteLater() ));   -  person Vern Jensen    schedule 12.07.2014
comment
ฉันคิดว่าคุณควรลบ QThread workerThread; ใน WorkerObject มันทำให้เกิดความเข้าใจผิด   -  person kien bui    schedule 07.05.2018


คำตอบ (5)


เกี่ยวกับสิ่งเดียวที่ฉันคิดได้ว่าจะเพิ่มคือการระบุเพิ่มเติมว่า QObjects มีความสัมพันธ์กับเธรดเดียว โดยปกติแล้วจะเป็นเธรดที่สร้าง QObject ดังนั้น หากคุณสร้าง QObject ในเธรดหลักของแอป และต้องการใช้ในเธรดอื่น คุณต้องใช้ moveToThread() เพื่อเปลี่ยนความสัมพันธ์

ซึ่งจะช่วยประหยัดเวลาในการต้องมีคลาสย่อย QThread และสร้างอ็อบเจ็กต์ของคุณในเมธอด run() จึงทำให้เนื้อหาของคุณถูกห่อหุ้มไว้อย่างสวยงาม

โพสต์บนบล็อกนั้นมีลิงก์ไปยังตัวอย่าง. มันค่อนข้างสั้นแต่แสดงให้เห็นแนวคิดพื้นฐาน สร้าง QObjects ของคุณ เชื่อมต่อสัญญาณของคุณ สร้าง QThread ของคุณ ย้าย QObjects ของคุณไปที่ QThread และเริ่มเธรด กลไกสัญญาณ/สล็อตจะช่วยให้มั่นใจได้ว่ามีการข้ามขอบเขตของเธรดอย่างเหมาะสมและปลอดภัย

คุณอาจต้องแนะนำการซิงโครไนซ์หากคุณต้องเรียกวิธีการบนวัตถุของคุณที่อยู่นอกกลไกนั้น

ฉันรู้ว่า Qt มีเครื่องมืออำนวยความสะดวกในการเธรดที่ดีอื่นๆ นอกเหนือจากเธรดที่ อาจจะคุ้มค่าที่จะทำความคุ้นเคย แต่ฉันยังไม่ได้ทำ :)

person Arnold Spence    schedule 04.11.2010
comment
ตัวอย่างที่เชื่อมโยงยังบอกด้วยว่าพวกเขาทำคลาสย่อย QThread และใช้งาน run() เพื่อทำ exec() โดยพื้นฐานแล้วจะเป็นการเริ่มต้นลูปเหตุการณ์และเปิดใช้งานการเชื่อมต่อเพื่อทำสิ่งที่พวกเขา... จากสิ่งที่ฉันรวบรวม คุณไม่จำเป็นต้องทำเช่นนั้น (จากโพสต์ต้นฉบับที่ฉันระบุไว้) หรือฉันเข้าใจผิดและคุณยังคงต้องทำ นี้? - person g19fanatic; 04.11.2010
comment
คุณเข้าใจถูกแล้ว ตั้งแต่ Qt 4.4 การใช้งานเริ่มต้นของ run() จะทำสิ่งนี้เพื่อคุณ - person Arnold Spence; 04.11.2010

นี่คือ ตัวอย่างหนึ่งของ วิธีใช้ QThread อย่างถูกต้อง แต่มีปัญหาบางประการ ซึ่งแสดงอยู่ในความคิดเห็น โดยเฉพาะอย่างยิ่ง เนื่องจากลำดับในการดำเนินการสล็อตไม่ได้ถูกกำหนดไว้อย่างเคร่งครัด จึงอาจทำให้เกิดปัญหาต่างๆ ได้ ความคิดเห็นที่โพสต์เมื่อวันที่ 6 สิงหาคม 2013 ให้แนวคิดที่ดีว่าจะจัดการกับปัญหานี้อย่างไร ฉันใช้อะไรทำนองนั้นในโปรแกรมของฉัน และนี่คือโค้ดตัวอย่างบางส่วนเพื่อชี้แจง

แนวคิดพื้นฐานก็เหมือนกัน: ฉันสร้างอินสแตนซ์ QThread ที่อยู่ในเธรดหลักของฉัน อินสแตนซ์คลาสผู้ปฏิบัติงานที่อยู่ในเธรดใหม่ที่ฉันสร้างขึ้น จากนั้นฉันจะเชื่อมต่อสัญญาณทั้งหมด

void ChildProcesses::start()
{
    QThread *childrenWatcherThread = new QThread();
    ChildrenWatcher *childrenWatcher = new ChildrenWatcher();
    childrenWatcher->moveToThread(childrenWatcherThread);
    // These three signals carry the "outcome" of the worker job.
    connect(childrenWatcher, SIGNAL(exited(int, int)),
            SLOT(onChildExited(int, int)));
    connect(childrenWatcher, SIGNAL(signalled(int, int)),
            SLOT(onChildSignalled(int, int)));
    connect(childrenWatcher, SIGNAL(stateChanged(int)),
            SLOT(onChildStateChanged(int)));
    // Make the watcher watch when the thread starts:
    connect(childrenWatcherThread, SIGNAL(started()),
            childrenWatcher, SLOT(watch()));
    // Make the watcher set its 'stop' flag when we're done.
    // This is performed while the watch() method is still running,
    // so we need to execute it concurrently from this thread,
    // hence the Qt::DirectConnection. The stop() method is thread-safe
    // (uses a mutex to set the flag).
    connect(this, SIGNAL(stopped()),
            childrenWatcher, SLOT(stop()), Qt::DirectConnection);
    // Make the thread quit when the watcher self-destructs:
    connect(childrenWatcher, SIGNAL(destroyed()),
            childrenWatcherThread, SLOT(quit()));
    // Make the thread self-destruct when it finishes,
    // or rather, make the main thread delete it:
    connect(childrenWatcherThread, SIGNAL(finished()),
            childrenWatcherThread, SLOT(deleteLater()));
    childrenWatcherThread->start();
}

พื้นหลังบางส่วน:

คลาส ChildProcesses เป็นตัวจัดการกระบวนการลูกที่เริ่มกระบวนการลูกใหม่ด้วยการเรียก spawn() เก็บรายการกระบวนการที่กำลังทำงานอยู่และอื่นๆ อย่างไรก็ตาม จำเป็นต้องติดตามสถานะลูก ซึ่งหมายถึงการใช้การเรียก waitpid() บน Linux หรือ WaitForMultipleObjects บน Windows ฉันเคยเรียกสิ่งเหล่านี้ในโหมดไม่บล็อกโดยใช้ตัวจับเวลา แต่ตอนนี้ฉันต้องการการตอบสนองที่รวดเร็วยิ่งขึ้น ซึ่งหมายถึงโหมดการบล็อก นั่นคือสิ่งที่ด้ายเข้ามา

คลาส ChildrenWatcher มีการกำหนดไว้ดังนี้:

class ChildrenWatcher: public QObject {
    Q_OBJECT
private:
    QMutex mutex;
    bool stopped;
    bool isStopped();
public:
    ChildrenWatcher();
public slots:
    /// This is the method which runs in the thread.
    void watch();
    /// Sets the stop flag.
    void stop();
signals:
    /// A child process exited normally.
    void exited(int ospid, int code);
    /// A child process crashed (Unix only).
    void signalled(int ospid, int signal);
    /// Something happened to a child (Unix only).
    void stateChanged(int ospid);
};

นี่คือวิธีการทำงาน เมื่อสิ่งเหล่านี้เริ่มต้นขึ้น ระบบจะเรียกเมธอด ChildProcess::start() (ดูด้านบน) มันสร้าง QThread ใหม่และ ChildrenWatcher ใหม่ ซึ่งจากนั้นจะถูกย้ายไปยังเธรดใหม่ จากนั้น ฉันเชื่อมโยงสัญญาณสามประการที่แจ้งให้ผู้จัดการของฉันทราบเกี่ยวกับชะตากรรมของกระบวนการย่อย (ออก/ส่งสัญญาณ/พระเจ้ารู้ดีว่าเกิดอะไรขึ้น) จากนั้นเริ่มความสนุกหลัก

ฉันเชื่อมต่อ QThread::started() กับเมธอด ChildrenWatcher::watch() ดังนั้นจึงเริ่มต้นทันทีที่เธรดพร้อม เนื่องจากผู้ดูอาศัยอยู่ในเธรดใหม่ นั่นคือที่ที่เมธอด watch() จะถูกดำเนินการ (การเชื่อมต่อที่อยู่ในคิวถูกใช้เพื่อเรียกสล็อต)

จากนั้น ฉันเชื่อมต่อสัญญาณ ChildProcesses::stopped() กับสล็อต ChildrenWatcher::stop() โดยใช้ Qt::DirectConnection เพราะฉันต้องดำเนินการแบบอะซิงโครนัส สิ่งนี้จำเป็นเพื่อให้เธรดของฉันหยุดเมื่อไม่จำเป็นต้องใช้ตัวจัดการ ChildProcesses อีกต่อไป วิธีการหยุด () มีลักษณะดังนี้:

void ChildrenWatcher::stop()
{
    mutex.lock();
    stopped = true;
    mutex.unlock();
}

แล้ว ChildrenWatcher::watch():

void ChildrenWatcher::watch()
{
  while (!isStopped()) {
    // Blocking waitpid() call here.
    // Maybe emit one of the three informational signals here too.
  }
  // Self-destruct now!
  deleteLater();
}

โอ้ และเมธอด isStopped() เป็นเพียงวิธีที่สะดวกในการใช้ mutex ในเงื่อนไข while():

bool ChildrenWatcher::isStopped()
{
    bool stopped;
    mutex.lock();
    stopped = this->stopped;
    mutex.unlock();
    return stopped;
}

สิ่งที่เกิดขึ้นที่นี่คือฉันตั้งค่าสถานะหยุดเมื่อฉันต้องการทำให้เสร็จ จากนั้นครั้งต่อไปที่ isStopped() จะถูกเรียกว่ามันจะคืนค่าเท็จและเธรดสิ้นสุด

แล้วจะเกิดอะไรขึ้นเมื่อ watch() วนซ้ำสิ้นสุดลง? มันเรียก DeleteLater() ดังนั้นวัตถุจะทำลายตัวเองทันทีที่การควบคุมถูกส่งกลับไปยังลูปเหตุการณ์ของเธรดซึ่งเกิดขึ้นทันทีหลังจากการเรียก DeleteLater() (เมื่อ watch() กลับมา) กลับไปที่ ChildProcesses::start() คุณจะเห็นว่ามีการเชื่อมต่อจากสัญญาณที่ถูกทำลาย() ของผู้เฝ้าดูไปยังช่อง exit() ของเธรด ซึ่งหมายความว่าเธรดจะเสร็จสิ้นโดยอัตโนมัติเมื่อผู้เฝ้าดูเสร็จสิ้น และเมื่อสร้างเสร็จแล้ว มันก็จะทำลายตัวเองด้วยเพราะสัญญาณที่เสร็จสิ้นแล้ว() ของมันเองนั้นเชื่อมต่อกับช่อง DeleteLater()

นี่เป็นแนวคิดเดียวกับที่ Maya โพสต์ แต่เนื่องจากฉันใช้สำนวนทำลายตัวเอง ฉันจึงไม่จำเป็นต้องขึ้นอยู่กับลำดับของการเรียกช่อง มันจะทำลายตัวเองก่อนเสมอ หยุดด้ายทีหลัง จากนั้นก็จะทำลายตัวเองด้วย ฉันสามารถกำหนดสัญญาณที่เสร็จสิ้นแล้ว() ในตัวผู้ปฏิบัติงาน จากนั้นเชื่อมต่อกับสัญญาณ DeleteLater() ของมันเอง แต่นั่นจะหมายถึงการเชื่อมต่อเพิ่มเติมเพียงครั้งเดียวเท่านั้น เนื่องจากฉันไม่ต้องการสัญญาณ Finish() เพื่อจุดประสงค์อื่น ฉันจึงเลือกที่จะเรียก DeleteLater() จากผู้ปฏิบัติงานเอง

Maya ยังกล่าวอีกว่าคุณไม่ควรจัดสรร QObjects ใหม่ในตัวสร้างของคนงานเพราะพวกมันจะไม่อยู่ในเธรดที่คุณย้ายคนงานไป ฉันจะบอกว่าทำมันต่อไปเพราะนั่นคือวิธีการทำงานของ OOP เพียงตรวจสอบให้แน่ใจว่า QObjects เหล่านั้นทั้งหมดเป็นลูกของผู้ปฏิบัติงาน (นั่นคือ ใช้ตัวสร้าง QObject(QObject*)) - moveToThread() ย้ายลูกทั้งหมดพร้อมกับวัตถุที่ถูกย้าย หากคุณต้องการมี QObjects ที่ไม่ใช่ลูกของอ็อบเจ็กต์ของคุณจริงๆ ให้แทนที่ MoveToThread() ในผู้ปฏิบัติงานของคุณเพื่อที่จะย้ายสิ่งที่จำเป็นทั้งหมดด้วย

person Sergei Tachenov    schedule 16.08.2013
comment
แม้ว่าฉันขอขอบคุณที่คุณแสดงการใช้งานผู้จัดการตามเหตุการณ์ แต่ก็ไม่เกี่ยวข้องกับคำถามนี้ คำถามเกี่ยวข้องกับความคลาดเคลื่อนของเอกสารระหว่างวิธีที่ Qt ใช้ในการแนะนำเธรดที่จะนำไปใช้และวิธีที่ 'เหมาะสม' ในการทำเช่นนั้น (ซึ่งตอนนี้ดีกว่าในเอกสารประกอบปัจจุบัน) ... - person g19fanatic; 19.08.2013
comment
@ g19 ฉันพบคำถามของคุณ (และหน้าอื่น ๆ อีกหลายหน้า) ในขณะที่ googling เพื่อหาวิธีที่ถูกต้องในการใช้ QThread หลังจากนั้นฉันก็ใช้สิ่งนี้ และจากนั้นฉันก็รู้ว่านี่คือสิ่งที่ฉันค้นหาใน Google ดังนั้นฉันจึงโพสต์ไว้ด้วยความหวังว่าคนอื่นที่กำลังค้นหาวิธีใช้ QThread ที่ถูกต้องจะพบว่าสิ่งนี้มีประโยชน์ - person Sergei Tachenov; 20.08.2013
comment
@ g19 โอ้ ฉันกำลังทำงานกับ Qt 4.6 หรืออะไรสักอย่าง ดังนั้นฉันจึงไม่รู้ว่าพวกเขาเปลี่ยนเอกสาร แต่เอกสารยังมีจำกัดมากและไม่ได้อธิบายวิธีการทำสิ่งที่ฉันต้องการ (และคนอื่นๆ อีกหลายคนจำเป็นต้องทำ) ดังนั้นฉันเดาว่าคำถามนี้ยังคงใช้ได้ - person Sergei Tachenov; 20.08.2013
comment
ตอนนี้พวกเขาโปรโมตถามคำถามที่พบบ่อยและตอบด้วยตัวเองสำหรับเนื้อหาประเภทนี้ใน SO ดังนั้นคุณอาจเปลี่ยนคำถามนี้เป็นคำถามแยกต่างหากได้ (ระบุรายละเอียดข้อผิดพลาดทั้งหมดที่คุณพบ) และยังได้รับการโหวตที่ดีอีกด้วย :) - person mlvljr; 27.10.2013
comment
ฉันพบว่าคำตอบของคุณมีประโยชน์มาก แต่จะดีถ้าคุณโพสต์โค้ดทั้งหมดไว้ที่ใดที่หนึ่ง (เช่น ส่วนสำคัญ) เพื่อดูรายละเอียดทั้งหมด - person parsley72; 27.04.2014
comment
@parsley ฉันเกรงว่ารหัสเต็มจะมีลิขสิทธิ์ และไม่ใช่โดยตัวฉันเองอย่างแน่นอน - person Sergei Tachenov; 28.04.2014

เพื่อไม่ให้เบี่ยงเบนไปจากคำตอบที่ยอดเยี่ยมของ @ sergey-tachenov แต่ใน Qt5 คุณสามารถหยุดใช้ SIGNAL และ SLOT ลดความซับซ้อนของโค้ดของคุณ และมีข้อได้เปรียบในการตรวจสอบเวลาคอมไพล์:

void ChildProcesses::start()
{
    QThread *childrenWatcherThread = new QThread();
    ChildrenWatcher *childrenWatcher = new ChildrenWatcher();
    childrenWatcher->moveToThread(childrenWatcherThread);
    // These three signals carry the "outcome" of the worker job.
    connect(childrenWatcher, ChildrenWatcher::exited,
            ChildProcesses::onChildExited);
    connect(childrenWatcher, ChildrenWatcher::signalled,
            ChildProcesses::onChildSignalled);
    connect(childrenWatcher, ChildrenWatcher::stateChanged,
            ChildProcesses::onChildStateChanged);
    // Make the watcher watch when the thread starts:
    connect(childrenWatcherThread, QThread::started,
            childrenWatcher, ChildrenWatcher::watch);
    // Make the watcher set its 'stop' flag when we're done.
    // This is performed while the watch() method is still running,
    // so we need to execute it concurrently from this thread,
    // hence the Qt::DirectConnection. The stop() method is thread-safe
    // (uses a mutex to set the flag).
    connect(this, ChildProcesses::stopped,
            childrenWatcher, ChildrenWatcher::stop, Qt::DirectConnection);
    // Make the thread quit when the watcher self-destructs:
    connect(childrenWatcher, ChildrenWatcher::destroyed,
            childrenWatcherThread, QThread::quit);
    // Make the thread self-destruct when it finishes,
    // or rather, make the main thread delete it:
    connect(childrenWatcherThread, QThread::finished,
            childrenWatcherThread, QThread::deleteLater);
    childrenWatcherThread->start();
}
person Community    schedule 27.04.2014

การแบ่งคลาสย่อยคลาส qthread จะยังคงเรียกใช้โค้ดในเธรดต้นทาง ฉันต้องการเรียกใช้ตัวฟัง udp ในแอปพลิเคชันที่ใช้ GUI Thread อยู่แล้ว (เธรดหลัก) และในขณะที่ตัวฟัง udp ของฉันทำงานได้อย่างสมบูรณ์แบบ GUI ของฉันก็ถูกหยุดเนื่องจากมันถูกบล็อกโดยตัวจัดการเหตุการณ์ qthread ที่คลาสย่อย ฉันคิดว่าสิ่งที่ g19fanatic โพสต์นั้นถูกต้อง แต่คุณจะต้องใช้เธรดผู้ปฏิบัติงานเพื่อย้ายวัตถุไปยังเธรดใหม่ได้สำเร็จ ฉันพบโพสต์นี้ ซึ่งอธิบายรายละเอียดสิ่งที่ควรทำและไม่ควรทำของเธรดใน QT

ต้องอ่านก่อนที่คุณจะตัดสินใจคลาสย่อย QThread !

person Community    schedule 18.08.2014
comment
ไม่จริง. รหัสที่ทำงานอยู่ในฟังก์ชันแทนที่ run() จะทำงานบนเธรดใหม่ - person Vincent; 04.05.2017
comment
จากเอกสาร Qt: สิ่งสำคัญคือต้องจำไว้ว่าอินสแตนซ์ QThread อยู่ในเธรดเก่าที่สร้างอินสแตนซ์ ไม่ใช่ในเธรดใหม่ที่เรียกใช้ run() ซึ่งหมายความว่าสล็อตที่อยู่ในคิวของ QThread ทั้งหมดจะดำเนินการในเธรดเก่า - person Vincent; 04.05.2017

โมเดลเธรดแนวปฏิบัติที่ดีที่สุดเวอร์ชันของฉันใน Qt5 นั้นเรียบง่ายดังนี้: worker.h:

/*
* This is a simple, safe and best-practice way to demonstrate how to use threads in Qt5.
* Copyright (C) 2019 Iman Ahmadvand
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* It is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

#ifndef _WORKER_H
#define _WORKER_H

#include <QtCore/qobject.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qthread.h>
#include <QtGui/qevent.h>

namespace concurrent {

    class EventPrivate;
    class Event : public QEvent {
    public:
        enum {
            EventType1 = User + 1
        };

        explicit Event(QEvent::Type);
        Event(QEvent::Type, const QByteArray&);

        void setData(const QByteArray&);
        QByteArray data() const;

    protected:
        EventPrivate* d;
    };

    class WorkerPrivate;
    /* A worker class to manage one-call and permanent tasks using QThread object */
    class Worker : public QObject {
        Q_OBJECT

    public:
        Worker(QThread*);
        ~Worker();

    protected slots:
        virtual void init();

    protected:
        bool event(QEvent*) override;

    protected:
        WorkerPrivate* d;

    signals:
        /* this signals is used for one call type worker */
        void finished(bool success);
    };

} // namespace concurrent

#endif // !_WORKER_H

worker.cpp:

/*
* This is a simple, safe and best-practice way to demonstrate how to use threads in Qt5.
* Copyright (C) 2019 Iman Ahmadvand
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* It is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

#include "worker.h"

using namespace concurrent;

class concurrent::EventPrivate {
public:
    QByteArray data;
};

Event::Event(QEvent::Type t):QEvent(t), d(new EventPrivate) {
    setAccepted(false);
}

Event::Event(QEvent::Type t, const QByteArray& __data) : Event(t) {
    setData(__data);
}

void Event::setData(const QByteArray& __data) {
    d->data = __data;
}

QByteArray Event::data() const {
    return d->data;
}



class concurrent::WorkerPrivate {
public:
    WorkerPrivate() {

    }
};

Worker::Worker(QThread* __thread) :QObject(nullptr), d(new WorkerPrivate) {
    moveToThread(__thread);

    QObject::connect(__thread, &QThread::started, this, &Worker::init);
    QObject::connect(this, &Worker::finished, __thread, &QThread::quit);
    QObject::connect(__thread, &QThread::finished, __thread, &QThread::deleteLater);
    QObject::connect(__thread, &QThread::finished, this, &Worker::deleteLater);
}

Worker::~Worker() {
    /* do clean up if needed */
}

void Worker::init() {
    /* this will called once for construction and initializing purpose */
}

bool Worker::event(QEvent* e) {
    /* event handler */
    if (e->type() == Event::EventType1) {
        /* do some work with event's data and emit signals if needed */
        auto ev = static_cast<Event*>(e);
        ev->accept();
    }
    return QObject::event(e);
}

usage.cpp:

#include <QtCore/qcoreapplication.h>
#include "worker.h"

using namespace concurrent;

Worker* create(bool start) {
    auto worker = new Worker(new QThread);
    if (start)
        worker->thread()->start();

    return worker;
}

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);
    auto worker = create(true);
    if (worker->thread()->isRunning()) {
        auto ev = new Event(static_cast<QEvent::Type>(Event::EventType1));
        qApp->postEvent(worker, ev, Qt::HighEventPriority);
    }
    return app.exec();
}
person IMAN4K    schedule 14.10.2019