2010-07-21 45 views
16

Tôi có một đối tượng trực tiếp được triển khai theo cách sau. Nó được sử dụng để thực hiện các tác vụ dài trong nền. Chuỗi chính gọi các tác vụ bằng cách gửi tín hiệu đến các vị trí công cộng (ví dụ: doTask). Đây là một ví dụ bị tước bỏ (không được kiểm tra).Gọi phương thức khe mà không cần kết nối?

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

(cần) này hoạt động như mong đợi miễn là doTask() được gọi bởi tín hiệu. Nhưng nếu thread chính gọi doTask() trực tiếp thì nó sẽ được thực hiện bởi luồng chính. Đối với một số tác vụ, tôi muốn thực thi thực thi theo chuỗi của đối tượng trực tiếp, ngay cả khi phương thức khe được gọi trực tiếp.

Tôi có thể thêm mã trước mặt doTask() để kiểm tra xem chuỗi hiện tại có m_thread trong trường hợp nó thực hiện phương thức hay không. Nếu không, tôi muốn rằng doTask() phát ra một tín hiệu đến 'this' để một lệnh gọi doTask() được xếp hàng đợi trong vòng lặp m_thread exec và được thực hiện bởi nó càng sớm càng tốt.

Tôi có thể làm như thế nào?

EDIT: Dựa trên câu trả lời được đề xuất, đây là mã mới. Phương thức doTask bây giờ ủy quyền thực hiện bởi luồng của objet trực tiếp, ngay cả khi được gọi trực tiếp bởi luồng chính. Được gọi bằng tín hiệu vẫn hoạt động như mong đợi.

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

Đây là triển khai đơn giản nhất mà tôi có thể tìm thấy để hỗ trợ thực thi phương thức không đồng bộ trong một chuỗi riêng biệt. Các invocations của các phương thức doTask() sẽ được xếp hàng đợi và xử lý ngay sau khi thread được bắt đầu. Khi được gọi từ chuỗi đối tượng, nó sẽ được thực thi ngay lập tức (không được xếp hàng đợi).

Lưu ý rằng tín hiệu start() chỉ được phát ra khi bắt đầu luồng. Điều này có nghĩa là doTask() gọi phương thức được xếp hàng đợi trước khi luồng được bắt đầu sẽ thực thi trước khi khe phương thức started() được gọi. Đây là lý do tôi gỡ bỏ nó từ việc thực hiện ban đầu. Do đó, khởi tạo đối tượng tốt hơn nên được thực hiện trong hàm tạo.

+0

tôi thấy một vấn đề ở đây: các tài liệu nói rằng bạn không thể di chuyển một đối tượng đến một thread nếu nó có một phụ huynh. Mã của bạn có hoạt động nếu MyTask được tạo với cha/mẹ không? – andref

+0

Một câu hỏi tương tự cũng đã được trả lời trong [QT + Cách gọi khe từ mã C++ tùy chỉnh đang chạy trong một luồng khác] (http: // stackoverflow.com/questions/1144240/qt-how-to-call-slot-từ-custom-c-code-running-in-a-chuỗi khác). – Trilarion

Trả lời

19

Bạn muốn gọi QMetaObject::invokeMethod để thực hiện việc này. Trong trường hợp của bạn, nó sẽ giống như thế

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

Điều này thật tuyệt vời. Nó hoạt động như một say mê. Xem Chỉnh sửa của tôi cho mã đã sử dụng. – chmike

+4

Có thể thực hiện điều này mà không sử dụng tên chuỗi cho phương pháp không? –

3

Về việc cải tiến duy nhất tôi muốn thêm là để tiết kiệm một số thời gian trên nhìn lên phương pháp:

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

Bạn nhận được bao nhiêu cho nỗ lực này? –

+0

Chữ ký của phương thức được chuyển đến 'indexOfMethod' nên bao gồm các đối số và ở dạng chuẩn hóa ([link] (https://qt-project.org/doc/qt-5.0/qtcore/qmetaobject.html#indexOfMethod)) mã đúng sẽ là 'm_doTaskImplIndex (metaObject() -> indexOfMethod (metaObject() -> normalizedSignature (" doTaskImpl (int) ")))' –

0

Vì vậy, làm thế nào về gói tất cả vào một lớp học tốt đẹp?
Tôi cũng đã thêm một vị trí finishPlease, phần này sẽ được thêm làm phần tử cuối cùng trong danh sách việc cần làm và cung cấp phản hồi cho chương trình chính khi nó thực sự xử lý tất cả thư đang chờ xử lý trước khi có thể bị giết.

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

Tôi nghi ngờ có lỗi trong MyTask. Nếu tôi đã hiểu internals Qt một cách chính xác sau đó

moveToThread(&m_thread);

sẽ thất bại nếu parent không phải là 0.

Các vấn đề liên quan