2010-11-04 12 views
54

QThread के लिए क्यूटी दस्तावेज QThread से एक वर्ग बनाने और रन विधि को लागू करने के लिए कहता है।QThread को लागू करने का सही तरीका क्या है ... (उदाहरण कृपया ...)

नीचे 4.7 QThread प्रलेखन से लिया जाता है ...

अपने स्वयं के धागे, उपवर्ग QThread बना सकते हैं और रन reimplement करने के लिए()। उदाहरण के लिए:

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

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

तो हर एक सूत्र में मैं बना लिया है, मैं सिर्फ इतना है कि किया है और सबसे बातों के लिए यह सिर्फ ठीक काम करता है (मैं अपने वस्तुओं में से किसी में moveToThread (यह) को लागू नहीं करते और यह बहुत अच्छा काम करता है)।

मैंने पिछले हफ्ते एक झगड़ा मारा (जहां मैंने अपनी वस्तुओं को बनाया, वहां काम करके इसे प्राप्त करने में कामयाब रहा) और following blog post पाया। यहां मूल रूप से कहा गया है कि QThread subclassing वास्तव में ऐसा करने का सही तरीका नहीं है (और यह कि दस्तावेज़ गलत है)।

यह एक क्यूटी डेवलपर से आ रहा है, इसलिए पहली नज़र में मुझे दिलचस्पी थी और आगे प्रतिबिंब पर, उससे सहमत हो। OO सिद्धांतों के बाद, आप वास्तव में केवल एक वर्ग उपवर्ग के लिए आगे उस वर्ग को बढ़ाने के लिए ... न सिर्फ सीधे वर्गों तरीकों का उपयोग करने के लिए ... thats तुम क्यों दृष्टांत ...

कहते हैं कि मैं एक कस्टम जाना चाहते थे चलें चाहते QObject वर्ग एक थ्रेड के लिए ... यह करने का 'सही' तरीका क्या होगा? उस ब्लॉग पोस्ट में, वह कहता है कि उसके पास कहीं उदाहरण है ... लेकिन अगर कोई मुझे यह समझा सकता है तो इसकी सराहना की जाएगी!

अद्यतन:

के बाद से यह सवाल इतना ध्यान हो जाता है, यहाँ एक QThread लागू करने के लिए 'उचित' तरीके के साथ एक प्रति और 4.8 प्रलेखन का पेस्ट है।

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 सदस्य है कि अनावश्यक है और उनके उदाहरण में कभी नहीं किया शामिल सार्थक है। उस टुकड़े को हटा दें और यह क्यूटी में थ्रेडिंग कैसे करें इसका एक उचित उदाहरण है।

+1

यह ध्यान देने योग्य है कि 4.8 राज्यों के तहत नए प्रलेखन एक QThread का उपयोग करें और सक्रिय रूप से करने के लिए उचित तरीके से एक QThread वस्तु से पाने सिर्फ एक कार्यकर्ता वस्तु/धागा बनाने के लिए के पुराने तरीके से हतोत्साहित करता है। http://qt-project.org/doc/qt-4.8/qthread.html#details – g19fanatic

+2

डॉक केवल शुरू QThread उपवर्ग में नये स्लॉट हतोत्साहित किया जाता है कहते हैं। QThread कक्षा से प्राप्त करने का कोई उल्लेख नहीं किया। QThread से व्युत्पन्न डेल्फी/सी ++ बिल्डर के टीटीएचड के समान प्रतिमान का पालन करता है। –

+2

http://woboq.com/blog/qthread-you-were-not-doing-so-wrong.html –

उत्तर

29

केवल एक चीज के बारे में जो मैं जोड़ना चाहता हूं, यह कहना है कि QObject के पास एक धागे के साथ संबंध है। यह आमतौर पर धागा है जो QObject बनाता है। इसलिए यदि आप ऐप के मुख्य थ्रेड में QObject बनाते हैं और इसे किसी अन्य थ्रेड में उपयोग करना चाहते हैं, तो आपको एफ़िनिटी बदलने के लिए moveToThread() का उपयोग करना होगा।

यह QThread उपclass करने और run() विधि में अपनी ऑब्जेक्ट्स बनाने के लिए सहेजता है, इस प्रकार आपकी सामग्री को अच्छी तरह से encapsulated रखने।

उस ब्लॉग पोस्ट में example का लिंक शामिल है। यह बहुत छोटा है लेकिन यह मूल विचार दिखाता है। अपना QObject एस बनाएं, अपने सिग्नल कनेक्ट करें, अपना QThread बनाएं, को QThread पर ले जाएं और थ्रेड प्रारंभ करें। सिग्नल/स्लॉट तंत्र यह सुनिश्चित करेंगे कि थ्रेड सीमाएं ठीक से और सुरक्षित रूप से पार हो जाएं।

यदि आपको उस तंत्र के बाहर अपने ऑब्जेक्ट पर विधियों को कॉल करना है तो आपको सिंक्रनाइज़ेशन शुरू करना पड़ सकता है।

मैं क्यूटी पता कुछ अन्य अच्छा threading facilities धागे कि शायद से परिचित हो रही लायक हैं परे है, लेकिन मैं अभी तक ऐसा करने के लिए :)

+0

लिंक किए गए उदाहरण में यह भी कहा गया है कि वे उप-वर्ग QThread करते हैं और exec() को निष्पादित करने के लिए लागू करते हैं()। यह मूल रूप से घटना पाश शुरू कर देंगे और उनके बात ... क्या करना है कि मैं क्या इकट्ठा से कनेक्शन को सक्षम, आप ऐसा करने के लिए (मूल पोस्ट मैं सूचीबद्ध से) की जरूरत नहीं होनी चाहिए या मैं गलत समझ रहा हूँ और आप अभी भी करने की आवश्यकता होगी इस? – g19fanatic

+1

आप सही ढंग से समझते हैं। क्यूटी 4.4 के अनुसार, रन() के डिफ़ॉल्ट कार्यान्वयन आपके लिए यह करता है। –

8

यहाँ one example of how to use QThread correctly है है, लेकिन यह इसके साथ कुछ मुद्दों पर है, जो में परिलक्षित होते हैं है टिप्पणियाँ। विशेष रूप से, जिस क्रम में स्लॉट निष्पादित किए जाते हैं, उसे सख्ती से परिभाषित नहीं किया जाता है, इससे विभिन्न समस्याएं हो सकती हैं। 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 वर्ग, एक बच्चे की प्रक्रिया प्रबंधक है कि अंडे के साथ नए बच्चे प्रक्रियाओं() कॉल शुरू होता है वर्तमान में चल रहे और इतने पर प्रक्रियाओं की सूची रहती है। हालांकि, इसे बच्चों के राज्यों का ट्रैक रखने की आवश्यकता है, जिसका मतलब है कि लिनक्स पर प्रतीक्षापिड() कॉल या विंडोज पर WaitForMultipleObjects का उपयोग करना है। मैं इन्हें टाइमर का उपयोग करके गैर-अवरुद्ध मोड में कॉल करता था, लेकिन अब मुझे अधिक त्वरित प्रतिक्रिया चाहिए, जिसका अर्थ ब्लॉकिंग मोड है। यह कैसे काम करता यहाँ

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); 
}; 

: यह जहां धागा भूमिका शुरू होती है

ChildrenWatcher वर्ग इस प्रकार परिभाषित किया गया है।। जब यह सब सामान शुरू हो जाता है, तो ChildProcess :: प्रारंभ() विधि को कॉल किया जाता है (ऊपर देखें)। यह एक नया QThread और एक नया ChildrenWatcher बनाता है, जिसे तब नए धागे में ले जाया जाता है। फिर मैं तीन संकेतों को जोड़ता हूं जो मेरे प्रबंधक को अपने बच्चों की प्रक्रियाओं के भाग्य के बारे में सूचित करते हैं (बाहर निकल/संकेतित/ईश्वर-जानता-क्या हुआ)। फिर मुख्य मज़ा शुरू होता है।

मैं QThread :: start() को ChildrenWatcher :: watch() विधि से कनेक्ट करता हूं, इसलिए जैसे ही धागा तैयार हो जाता है, इसे शुरू किया जाता है। चूंकि दर्शक नए धागे में रहता है, वहीं जहां घड़ी() विधि निष्पादित की जाती है (स्लॉट कनेक्शन स्लॉट को कॉल करने के लिए उपयोग किया जाता है)।

फिर मैं Qt :: DirectConnection का उपयोग कर ChildWroccher :: stop() स्लॉट को ChildProcesses :: stop() सिग्नल कनेक्ट करता हूं क्योंकि मुझे इसे अतुल्यकालिक रूप से करने की आवश्यकता है। इसकी आवश्यकता है इसलिए जब मेरा चाइल्डप्रोसेस मैनेजर की आवश्यकता नहीं है तो मेरा धागा बंद हो जाता है। बंद() विधि इस प्रकार है:

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

और फिर ChildrenWatcher :: घड़ी():

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

ओह, और isStopped() विधि में एक म्युटेक्स उपयोग करने के लिए सिर्फ एक सुविधाजनक तरीका है जबकि() हालत:

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

तो क्या यहाँ होता है मैं बंद कर दिया ध्वज सेट कि जब मैं खत्म करने की जरूरत है, और फिर अगली बार isStopped() यह गलत वापस आती है और धागा समाप्त होता है कहा जाता है।

तो क्या होता है जब घड़ी() लूप समाप्त होता है? यह deleteLater() को कॉल करता है, इसलिए ऑब्जेक्ट स्वयं को नष्ट कर देता है जैसे ही नियंत्रण थ्रेड इवेंट लूप पर वापस आ जाता है जो हटाए जाने के बाद ठीक होता है() कॉल (जब घड़ी() रिटर्न)। ChildProcesses पर वापस जाएं :: प्रारंभ करें(), आप देख सकते हैं कि थैचर के हटाए गए() स्लॉट से वॉचर के नष्ट() सिग्नल से कनेक्शन है। इसका मतलब यह है कि जब वॉचर किया जाता है तो थ्रेड स्वचालित रूप से खत्म हो जाता है।और जब यह समाप्त हो जाता है, तो यह भी स्वयं को नष्ट कर देता है क्योंकि इसका अपना पूरा() सिग्नल इसके डिलीटलाटर() स्लॉट से जुड़ा होता है।

यह काफी एक ही विचार के रूप में माया तैनात है, लेकिन क्योंकि मैं आत्म विनाश मुहावरा इस्तेमाल करते हैं, मैं अनुक्रम जिसमें स्लॉट कहा जाता है पर निर्भर करते हैं की जरूरत नहीं है। यह हमेशा पहले स्वयं को नष्ट कर देता है, बाद में थ्रेड रोकता है, फिर यह स्वयं भी नष्ट हो जाता है। मैं कार्यकर्ता में एक पूर्ण() सिग्नल को परिभाषित कर सकता हूं, और फिर इसे अपने स्वयं के डिलीटलाटर() से कनेक्ट कर सकता हूं, लेकिन इसका मतलब केवल एक कनेक्शन होगा। चूंकि मुझे किसी अन्य उद्देश्य के लिए एक पूर्ण() सिग्नल की आवश्यकता नहीं है, इसलिए मैंने केवल कार्यकर्ता से हटाना() को कॉल करना चुना है।

माया भी कहा गया है कि आप कार्यकर्ता निर्माता में नए QObjects आवंटित नहीं चाहिए क्योंकि वे धागा आप के लिए कार्यकर्ता ले जाने में नहीं रहना होगा। मैं कहूंगा कि वैसे भी ऐसा करें क्योंकि ओओपी काम करता है। बस सुनिश्चित करें कि उन सभी QObjects कार्यकर्ता के बच्चों (अर्थात, QObject (QObject *) निर्माता का उपयोग है) कर रहे हैं - moveToThread() वस्तु ले जाया जा रहा के साथ-साथ सभी बच्चों को ले जाता है। यदि आपको वास्तव में QObjects की आवश्यकता है जो आपके ऑब्जेक्ट के बच्चे नहीं हैं, तो अपने कार्यकर्ता में moveToTread() को ओवरराइड करें ताकि यह सभी आवश्यक सामान भी चला सके।

+0

जबकि मैं आपको घटना-आधारित प्रबंधक के कार्यान्वयन को दिखाने की सराहना करता हूं, यह इस प्रश्न से संबंधित नहीं है। सवाल के बीच कैसे क्यूटी सिफारिश करने के लिए सूत्र लागू किया जा इस्तेमाल किया प्रलेखन विसंगति और 'उचित' जिस तरह से ऐसा करने के लिए (जो अब बेहतर है वर्तमान दस्तावेज में) के संबंध में था ... – g19fanatic

+1

@ G19, मैं आपके सवाल का (मिल गया है और कई अन्य पेज) QThread का उपयोग करने के सही तरीके के लिए googling जबकि। उसके बाद ही मैंने इसे लागू किया, और फिर मुझे एहसास हुआ कि यह वही है जो मैं के लिए गुगल रहा था। तो मैंने आशा में यह पोस्ट किया कि कोई और जो QThread का उपयोग करने के लिए सही तरीके से गुजर रहा है, उसे यह उपयोगी लगेगा। –

+0

@ G19, ओह, और मैं क्यूटी 4.6 या कुछ और के साथ काम कर रहा था, तो मैं पता नहीं है कि वे डॉक्स बदल दिया था।लेकिन दस्तावेज़ अभी भी बहुत सीमित हैं और मुझे यह समझाने की ज़रूरत नहीं है कि मुझे क्या करना है (और कई अन्य लोगों को क्या करना है), इसलिए मुझे लगता है कि सवाल अभी भी मान्य है। –

3

@ सेर्गेई-tachenov उत्तम जवाब में कोई कमी नहीं है, लेकिन Qt5 में आप, सिग्नल और स्लॉट का उपयोग बंद अपने कोड को सरल बनाने और संकलन समय की जाँच का लाभ हो सकता है:

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(); 
} 
2

QThread वर्ग होगा उपवर्गीकरण अभी भी मूल धागे में कोड चलाएं। मैं आवेदन पत्र में एक यूडीपी श्रोता जो पहले से ही जीयूआई थ्रेड (मुख्य थ्रेड) का उपयोग किया जाता है को चलाने के लिए चाहता था और जब तक मेरी यूडीपी श्रोता पूरी तरह से काम कर रहा था मेरी जीयूआई जम गया के रूप में यह subclassed QThread ईवेंट हैंडलर्स द्वारा अवरोधित की गई। मुझे लगता है कि जी 1 फाइनैटिक पोस्ट क्या सही है, लेकिन आपको ऑब्जेक्ट थ्रेड को नए थ्रेड पर सफलतापूर्वक माइग्रेट करने के लिए वर्क थ्रेड की भी आवश्यकता होगी। मुझे this पोस्ट मिला जो क्यूटी में थ्रेडिंग के डू और डोंट के विवरण में वर्णन करता है।

इससे पहले कि आप QThread उपवर्ग का फैसला अवश्य पढ़ें!

+1

सच नहीं है। ओवरराइड फ़ंक्शन रन() में चल रहा कोड नए थ्रेड पर चलाएगा। – Vincent

+0

क्यूटी दस्तावेज़ों से: यह याद रखना महत्वपूर्ण है कि एक QThread उदाहरण पुराने थ्रेड में रहता है जो इसे तुरंत चालू करता है, न कि नए थ्रेड में जो रन() कहते हैं। इसका मतलब है कि QThread के सभी कतारबद्ध स्लॉट पुराने धागे में निष्पादित होंगे। – Vincent

संबंधित मुद्दे