Tengo un objeto vivo implementado de la siguiente manera. Se usa para ejecutar tareas largas en segundo plano. El hilo principal invoca las tareas enviando una señal a las ranuras públicas (es decir, doTask). Aquí hay un ejemplo reducido (no probado).¿Invocar método de ranura sin conexión?
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);
}
Esto (debería) funcionar como se espera siempre que doTask() sea invocado por una señal. Pero si el hilo principal llama a doTask() directamente, entonces será ejecutado por el hilo principal. Para algunas tareas, quiero aplicar una ejecución por el hilo del objeto en vivo, incluso si el método de ranura se llama directamente.
Podría agregar el código delante de doTask() para comprobar si el hilo actual es m_thread en cuyo caso se ejecuta el método. De lo contrario, me gustaría que doTask() emita una señal a 'esto' para que una invocación de doTask() se ponga en cola en el bucle m_thread exec y se ejecute lo antes posible.
¿Cómo podría hacer eso?
EDIT: De acuerdo con la respuesta propuesta, aquí está el nuevo código. El método doTask ahora delega la ejecución por el hilo del objeto vivo, incluso si es invocado directamente por el hilo principal. Llamado por señal todavía funciona como se esperaba.
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);
}
Esta es la implementación más simple que pude encontrar para soportar ejecuciones de métodos asíncronos en un hilo separado. Las invocaciones de los métodos doTask() se pondrán en cola y se procesarán tan pronto como se inicie el subproceso. Cuando se llama desde el hilo del objeto, se ejecutará inmediatamente (no en cola).
Tenga en cuenta que la señal de inicio() se emite solo cuando se inicia la secuencia. Esto significa que la invocación del método doTask() en cola antes de que se inicie el hilo se ejecutará antes de invocar el intervalo del método started(). Esta es la razón por la que lo eliminé de la implementación inicial. La inicialización de objetos debería realizarse preferiblemente en el constructor.
veo un problema en este caso: los documentos dicen que no se puede mover un objeto a otro hilo si tiene un padre ¿Su código funciona si MyTask se crea con un padre? – andref
Ya se respondió una pregunta similar en [QT + Cómo llamar a la ranura del código personalizado de C++ que se ejecuta en una conversación diferente] (http: // stackoverflow.com/questions/1144240/qt-how-to-call-slot-from-custom-c-code-running-in-a-different-thread). – Trilarion