2010-07-21 11 views
16

मेरे पास निम्नलिखित तरीके से लागू एक लाइव ऑब्जेक्ट है। इसका उपयोग पृष्ठभूमि में लंबे कार्यों को निष्पादित करने के लिए किया जाता है। मुख्य धागा सार्वजनिक स्लॉट (यानी DoTask) को सिग्नल भेजकर कार्यों को आमंत्रित करता है। यहां एक छीन लिया गया उदाहरण है (परीक्षण नहीं किया गया)।बिना कनेक्शन के स्लॉट विधि को आमंत्रित करें?

class MyTask : public QObject 
{ 
    Q_OBJECT 

public: 
    MyTask(); 
    ~MyTask(); 

public slots: 
    void doTask(int param); 

private slots: 
    void stated(); 

signals: 
    void taskCompleted(int result); 

private: 
    QThread m_thread; 
}; 


MyTask::MyTask() 
{ 
    moveToThread(&m_thread); 
    connect(&m_thread, SIGNAL(started()), this, SLOT(started())); 
    m_thread.start(); 
} 

MyTask::~MyTask() 
{ 
    // Gracefull thread termination (queued in exec loop) 
    if(m_thread.isRunning()) 
    { 
     m_thread.quit(); 
     m_thread.wait(); 
    } 
} 

void MyTask::started() 
{ 
    // initialize live object 
} 

void MyTask::doTask(int param) 
{ 
    sleep(10); 
    emit taskCompleted(param*2); 
} 

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

मैं यह जांचने के लिए डॉट टास्क() के सामने कोड जोड़ सकता हूं कि वर्तमान धागा m_thread है, जिस स्थिति में यह विधि निष्पादित करता है। यदि नहीं, तो मैं चाहता हूं कि DoTask() 'इस' के लिए एक सिग्नल उत्सर्जित करता है ताकि mTthread exec लूप में doTask() का आविष्कार किया जा सके और जितनी जल्दी हो सके इसे निष्पादित किया जाए।

मैं यह कैसे कर सकता हूं?

संपादित करें: प्रस्तावित उत्तर के आधार पर, यह नया कोड है। DoTask विधि अब लाइव ओब्जेट के थ्रेड द्वारा निष्पादन का प्रतिनिधित्व करती है, भले ही मुख्य थ्रेड द्वारा सीधे कहा जाता है। सिग्नल द्वारा बुलाया अभी भी उम्मीद के रूप में काम करता है।

class MyTask : public QObject 
{ 
    Q_OBJECT 

public: 
    explicit MyTask(QObject *parent = 0); 
    ~MyTask(); 

public slots: 
    void doTask(int param); 

private slots: 
    void doTaskImpl(int param); 

signals: 
    void taskCompleted(int result); 

private: 
    QThread m_thread; 
}; 

MyTask::MyTask(QObject *parent) : QObject(parent) 
{ 
    moveToThread(&m_thread); 
    m_thread.start(); 
} 

MyTask::~MyTask() 
{ 
    // Gracefull thread termination (queued in exec loop) 
    if(m_thread.isRunning()) 
    { 
     m_thread.quit(); 
     m_thread.wait(); 
    } 
} 

void MyTask::doTask(int param) 
{ 
    QMetaObject::invokeMethod(this, "doTaskImpl", Q_ARG(int, param)); 
} 

void MyTask::doTaskImpl(int param) 
{ 
    // Do the live oject's asynchronous task 
    sleep(10); 
    emit taskCompleted(param*2); 
} 

यह एक साधारण थ्रेड में एसिंक्रोनस विधि निष्पादन का समर्थन करने के लिए सबसे सरल कार्यान्वयन है। जैसे ही धागा शुरू होता है, doTask() विधियों के आविष्कार कतारबद्ध और संसाधित किए जाएंगे। जब ऑब्जेक्ट थ्रेड से बुलाया जाता है, तो इसे तुरंत निष्पादित किया जाएगा (कतारबद्ध नहीं)।

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

+0

मैं एक समस्या यहाँ देख नहीं है: डॉक्स का कहना है कि आप अगर यह एक और धागा करने के लिए एक वस्तु को स्थानांतरित नहीं किया जा सकता एक माता पिता है यदि आपका टाटा माता-पिता के साथ बनाया गया है तो क्या आपका कोड काम करता है? – andref

+0

एक समान प्रश्न का उत्तर पहले ही दिया गया था [क्यूटी + कस्टम सी ++ कोड से अलग थ्रेड में चलने वाले स्लॉट को कैसे कॉल करें] (http: // stackoverflow।com/प्रश्न/1144240/क्यूटी-कैसे-टू-कॉल-स्लॉट-से-कस्टम सी कोड से चल में एक-अलग धागा)। – Trilarion

उत्तर

19

आप ऐसा करने के लिए QMetaObject::invokeMethod पर कॉल करना चाहते हैं। आपके मामले में, यह कुछ ऐसा दिखाई देगा

MyTask *task; 
int param; 
// ... 
// Will automatically change threads, if needed, to execute 
// the equivalent of: 
// (void)task->doTask(param); 
QMetaObject::invokeMethod(task, "doTask", Q_ARG(int, param)); 
+0

यह कमाल है। यह एक सम्मोहन की तरह काम करता है। प्रयुक्त कोड के लिए मेरा संपादन देखें। – chmike

+4

क्या विधि के लिए स्ट्रिंग नाम का उपयोग किए बिना ऐसा करना संभव है? –

3

मैं जोड़ना होगा केवल सुधार के बारे में की तरह विधि को देख पर कुछ समय बचाने के लिए है:

class MyTask { 
// ... 
private: 
    int m_doTaskImplIndex; 
}; 

MyTask::MyTask() : 
    //... 
    m_doTaskImplIndex(metaObject()->indexOfMethod("doTaskImpl")) 
    //... 
{} 

void MyTask::doTask(int param) 
{ 
    metaObject()->method(m_doTaskImplIndex).invoke(this, Q_ARG(int, param)); 
} 
+1

इस अतिरिक्त प्रयास से आप कितना लाभ प्राप्त करते हैं? –

+0

'indexOfMethod' को पारित विधि के हस्ताक्षर में तर्क शामिल होना चाहिए और सामान्यीकृत रूप में होना चाहिए ([link] (https://qt-project.org/doc/qt-5.0/qtcore/qmetaobject.html#indexOfMethod)) तो सही कोड 'm_doTaskImplIndex (मेटाऑब्जेक्ट() -> indexOfMethod (मेटाऑब्जेक्ट() -> सामान्यीकृत हस्ताक्षर ("doTaskImpl (int)") होना चाहिए) '' –

0

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

class Threaded : public QObject 
{ 
    Q_OBJECT 
public: 
    Threaded() { 
     thread = new QThread(this); 
     this->moveToThread(thread); 
     connect(thread, SIGNAL(started()), this, SLOT(init()), \ 
                Qt::QueuedConnection); 
     thread->start(); 
    } 

    virtual ~Threaded() { 
     thread->exit(); 
     thread->wait(); 
     delete thread; 
    } 

signals: 
    void okayKillMe(); 

public slots: 
    virtual void init() = 0; 
    void finishPlease() {emit okayKillMe();} 

protected: 
    QThread* thread; 
}; 

class MyClass : public Threaded 
{ 
    Q_OBJECT 
public: 
    MyClass() { } 
    virtual ~MyClass() { } 

public slots: 
    void init() { } 
    void doStuff() { } 
    void doOtherStuff(int* data) { } 

}; 
1

मुझे संदेह है कि माईटास्क में एक बग है। मैं क्यूटी internals समझ में आ गया है सही ढंग से तो

moveToThread(&m_thread);

असफल हो जायेगी अगर parent 0.

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