У меня есть живой объект, реализованный следующим образом. Он используется для выполнения длинных задач в фоновом режиме. Основной поток вызывает задачи путем отправки сигнала в общедоступные слоты (т. Е. 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() вызывается сигналом. Но если основной поток вызывает doTask() напрямую, он будет выполняться основным потоком. Для некоторых задач я хочу принудительно выполнить выполнение потоком живого объекта, даже если метод слота вызывается напрямую.
Я могу добавить код перед doTask(), чтобы проверить, является ли текущий поток m_thread, в этом случае он выполняет этот метод. Если нет, мне бы хотелось, чтобы doTask() выдавал сигнал этому «так», чтобы вызов doTask() помещался в очередь в цикле exec m_thread и выполнялся им как можно скорее.
Как я мог это сделать?
EDIT: На основании предложенного ответа, вот новый код. Метод doTask теперь делегирует выполнение потоком live objet, даже если он вызван непосредственно основным потоком. Вызывается сигналом, который работает по-прежнему.
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() будут поставлены в очередь и обработаны, как только начнется поток. При вызове из потока объектов он будет выполнен немедленно (не в очереди).
Обратите внимание, что сигнал start() испускается только при запуске нити. Это означает, что вызов метода doTask(), поставленный перед запуском потока, будет выполняться до вызова слота метода start(). Именно по этой причине я удалил его из первоначальной реализации. Таким образом, инициализация объекта предпочтительно должна выполняться в конструкторе.
Я вижу проблему здесь: документы говорят, что вы не можете переместить объект на другой поток, если он имеет родителя. Работает ли ваш код, если MyTask создан с родителем? – andref
Аналогичный вопрос уже был дан в [QT + Как позвонить слоту из пользовательского кода C++, запущенного в другом потоке] (http: // stackoverflow.ком/вопросы/1144240/кварта-как к вызову слота-из-обычая-с-код выполняющегося-в-другой-нити). – Trilarion