una traves todos thread subprocesos subproceso seguras requiere realizar que para otro metodo make los llamó llamar llamadas interfaz how función formularios evaluación ejecuten diferente desde cross controles control como checkforillegalcrossthreadcalls calls aplicación aplanó acceder c++ multithreading qt qtconcurrent

c++ - traves - metodo invoke c#



¿Qué sucede con la afinidad de subprocesos de un QObject creado en un subproceso de trabajo que luego finaliza? (6)

Digamos que llamo a QtConcurrent::run() que ejecuta una función en un hilo de trabajo, y en esa función asigno dinámicamente varios QObjects (para uso posterior). Dado que se crearon en el hilo de trabajo, su afinidad de hilo debe ser la del hilo de trabajo. Sin embargo, una vez que finaliza el subproceso de trabajo, la afinidad de subprocesos de QObject ya no debe ser válida.

La pregunta: ¿Qt mueve automáticamente los QObjects al hilo principal, o somos responsables de moverlos a un hilo válido antes de que termine el hilo del trabajador?


¿Qt mueve automáticamente los QObjects al hilo principal o somos responsables de moverlos a un hilo válido antes de que termine el hilo del trabajador?

No , Qt no mueve QObject automáticamente en el hilo padre.

Este comportamiento no está explícitamente documentado, así que hice una pequeña investigación del código fuente de Qt framework, rama principal.

QThread inicia en QThreadPrivate::start :

unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(void *arg) { ... thr->run(); finish(arg); return 0; }

QThreadPrivate::start :

void QThread::terminate() { Q_D(QThread); QMutexLocker locker(&d->mutex); if (!d->running) return; if (!d->terminationEnabled) { d->terminatePending = true; return; } TerminateThread(d->handle, 0); d->terminated = true; QThreadPrivate::finish(this, false); }

En ambos casos, la finalización del hilo se realiza en QThreadPrivate::start :

void QThreadPrivate::finish(void *arg, bool lockAnyway) { QThread *thr = reinterpret_cast<QThread *>(arg); QThreadPrivate *d = thr->d_func(); QMutexLocker locker(lockAnyway ? &d->mutex : 0); d->isInFinish = true; d->priority = QThread::InheritPriority; bool terminated = d->terminated; void **tls_data = reinterpret_cast<void **>(&d->data->tls); locker.unlock(); if (terminated) emit thr->terminated(); emit thr->finished(); QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); QThreadStorageData::finish(tls_data); locker.relock(); d->terminated = false; QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher; if (eventDispatcher) { d->data->eventDispatcher = 0; locker.unlock(); eventDispatcher->closingDown(); delete eventDispatcher; locker.relock(); } d->running = false; d->finished = true; d->isInFinish = false; if (!d->waiters) { CloseHandle(d->handle); d->handle = 0; } d->id = 0; }

QEvent::DeferredDelete evento QEvent::DeferredDelete para la limpieza de QObject::deleteLater , luego de limpiar los datos de TLS con QThreadStorageData::finish(tls_data) y eventDispatcher eliminados. Después de eso, QObject no recibirá eventos de este hilo, pero la afinidad de subprocesos de QObject mantiene igual. Es interesante ver la implementación de void QObject::moveToThread(QThread *targetThread) para comprender cómo cambia la afinidad de subprocesos.

La implementación de void QThreadPrivate::finish(void *arg, bool lockAnyway) deja en claro que QThread no cambia la afinidad de subprocesos de QThread .


Sin embargo, una vez que finaliza el subproceso de trabajo, la afinidad de subprocesos de QObject ya no debe ser válida.

El hilo de trabajo NO termina después de su llamada de función. El objetivo de utilizar QtConcurrent::run es ejecutar un gran número de tareas pequeñas en el grupo de subprocesos global (o algunas proporcionadas QThreadPool ) mientras se reutilizan subprocesos para evitar la sobrecarga de crear y destruir subprocesos para cada una de estas pequeñas tareas. Además de distribuir el cómputo en todos los núcleos disponibles.

Puede intentar ver el código fuente de Qt para ver cómo se implementa QtConcurrent::run . Verá que termina llamando a RunFunctionTaskBase::start , que básicamente llama a QThreadPool::start con un QRunnable que llama a la función que se pasó inicialmente a QtConcurrent::run .

Ahora el punto al que quiero llegar es que QThreadPool::start se implemented agregando QRunnable a una cola y luego tratando de QRunnable uno de los subprocesos del grupo de subprocesos (que están waiting que se QRunnable un nuevo QRunnable agregado a la cola). Lo que hay que notar aquí, es que los subprocesos del grupo de subprocesos no están ejecutando un bucle de eventos (no están diseñados para actuar de esta manera), están ahí solo para ejecutar QRunnable s en la cola y nada más (se implementan de esta manera por razones de rendimiento obviamente).

Esto significa que, en el momento en que está creando un objeto QObject en una función ejecutada en QtConcurrent::run , solo está creando un QObject que vive en un hilo sin bucle de evento, de los docs , las restricciones incluyen:

Si no se está ejecutando ningún bucle de evento, los eventos no se entregarán al objeto. Por ejemplo, si crea un objeto QTimer en un hilo pero nunca llama a exec() , el QTimer nunca emitirá su señal de timeout() . Llamar a deleteLater() tampoco funcionará. (Estas restricciones se aplican también al hilo principal).

TL; DR: QtConcurrent::run ejecuta funciones en subprocesos del QThreadPool global (o uno proporcionado). Esos subprocesos no ejecutan un bucle de evento, simplemente esperan que se ejecute QRunnable s. Entonces, un QObject viva en un hilo de estos hilos no recibe ningún evento.

En la documentation , han puesto QThread (posiblemente, con un bucle de evento y un objeto de trabajador) y el uso de QtConcurrent::run como dos tecnologías separadas de multi-threading. No están destinados a mezclarse. Entonces, ningún trabajador objeta en grupos de hilos , esto es solo pedir problemas.

La pregunta: ¿Qt mueve automáticamente los QObjects al hilo principal, o somos responsables de moverlos a un hilo válido antes de que termine el hilo del trabajador?

Creo que después de ver las cosas de esta manera, la respuesta es obvia que Qt NO mueve el QObject en ningún subproceso automáticamente. La documentación ha advertido sobre el uso de un QObject en un QThread sin un bucle de evento, y eso es todo.

Usted es libre de moverlos al hilo que desee. Pero tenga en cuenta que moveToThread() veces puede causar problemas. Por ejemplo, si mover su objeto de trabajo implica mover un QTimer :

Tenga en cuenta que todos los temporizadores activos para el objeto se restablecerán. Los temporizadores primero se detienen en el subproceso actual y se reinician (con el mismo intervalo) en el targetThread. Como resultado, mover constantemente un objeto entre hilos puede posponer eventos de temporizador indefinidamente.

Conclusión: creo que deberías considerar usar tu propio QThread que ejecuta su ciclo de eventos, y crear tus QObject trabajadores allí en lugar de utilizar QtConcurrent . De esta forma es mucho mejor que mover QObject s, y puede evitar muchos errores que pueden surgir del uso de su enfoque actual. Eche un vistazo a la documentation y elija la tecnología que mejor se adapte a su caso de uso. Solo use QtConcurrent si solo desea ejecutar una función de llamada única y obtener su valor de retorno. Si desea una interacción permanente con el hilo, debe cambiar a usar su propio QThread con QObject s de trabajador.


A pesar de que los documentos de Qt no parecen especificar el comportamiento que se puede encontrar haciendo un seguimiento de lo que devuelve QObject::thread() antes y después de que termine el hilo.


Aunque esta es una pregunta antigua, hace poco hice la misma pregunta y la respondí usando QT 4.8 y algunas pruebas.

AFAIK no puedes crear objetos con un padre de una función QtConcurrent :: run. He intentado las siguientes dos formas. Permítanme definir un bloque de código, luego exploraremos el comportamiento seleccionando POINTER_TO_THREAD.

Algún código de psuedo te mostrará mi prueba

Class MyClass : public QObject { Q_OBJECT public: doWork(void) { QObject* myObj = new QObject(POINTER_TO_THREAD); .... } } void someEventHandler() { MyClass* anInstance = new MyClass(this); QtConcurrent::run(&anInstance, &MyClass::doWork) }

Ignorando posibles problemas de alcance ...

Si POINTER_TO_THREAD está configurado a this , recibirá un error porque se resolverá con un puntero al objeto anInstance que vive en el hilo principal, no con el hilo que QtConcurrent ha enviado para él. Verás algo como ...

Cannot create children for a parent in another thread. Parent: anInstance, parents thread: QThread(xyz), currentThread(abc)

Si POINTER_TO_THREAD está establecido en QObject::thread() , entonces obtendrá un error porque se resolverá con el objeto QThread en el que vive anInstance , y no con el hilo que QtConcurrent ha enviado para él. Verás algo como ...

Cannot create children for a parent in another thread. Parent: QThread(xyz), parents thread: QThread(xyz), currentThread(abc)

Espero que mi prueba sea útil para otra persona. Si alguien sabe cómo obtener un puntero al QThread en el que QtConcurrent ejecuta el método, ¡me interesaría escucharlo!


No estoy seguro si Qt cambia automáticamente la afinidad del hilo. Pero incluso si lo hace, el único hilo razonable para moverse es el hilo principal. Yo los empujaría al final de la función de enhebrado yo mismo.

myObject->moveToThread(QApplication::instance()->thread());

Ahora esto solo importa si los objetos hacen uso de un proceso de evento como las señales de envío y recepción.


QThread no está documentado para mover automáticamente cualquier QObject cuando termina, por lo que creo que ya podemos concluir que no existe tal cosa. Tal comportamiento sería muy sorprendente, y en desacuerdo con el resto de la API.

Para completar, probé con Qt 5.6:

QObject o; { QThread t; o.moveToThread(&t); for (int i = 0; i < 2; ++i) { t.start(); QVERIFY(t.isRunning()); QVERIFY(o.thread() == &t); t.quit(); t.wait(); QVERIFY(t.isFinished()); QVERIFY(o.thread() == &t); } } QVERIFY(o.thread() == nullptr);

Recuerde que un QThread no es un hilo, sino que gestiona un hilo.

Cuando finaliza un QThread , continúa existiendo y los objetos que viven en él continúan viviendo en él, pero ya no procesan eventos. El QThread se puede reiniciar (no se recomienda), en cuyo punto se reanudará el procesamiento del evento (por lo que el mismo QThread podría estar administrando un hilo diferente).

Cuando se destruye un QThread , los objetos que vivían en él dejan de tener afinidad con el hilo. La documentation no garantiza esto, y de hecho dice "Debe asegurarse de que todos los objetos creados en un hilo se eliminen antes de eliminar el QThread ".

Digamos que llamo a QtConcurrent::run() que ejecuta una función en un hilo de trabajo, y en esa función asigno dinámicamente varios QObjects (para uso posterior). Dado que se crearon en el hilo de trabajo, su afinidad de hilo debe ser la del hilo de trabajo. Sin embargo, una vez que finaliza el subproceso de trabajo, la afinidad de subprocesos de QObject ya no debe ser válida.

El QThread no termina en este escenario. Cuando finaliza una tarea generada por QtConcurrent::run , el QThread se estaba ejecutando se devuelve al QThreadPool y puede reutilizarse mediante una llamada posterior a QtConcurrent::run , y los QObject viven en ese QThread continúan viviendo allí.

QThreadPool::globalInstance()->setMaxThreadCount(1); QObject *o = nullptr; QThread *t = nullptr; QFuture<void> f = QtConcurrent::run([&] { o = new QObject; t = o->thread(); QVERIFY(t == QThread::currentThread()); }); f.waitForFinished(); QVERIFY(t == o->thread()); QVERIFY(t->isRunning()); f = QtConcurrent::run([=] { QVERIFY(t == QThread::currentThread()); }); f.waitForFinished();

Es posible que desee mover manualmente un objeto de un QThread antes de que se devuelva a QThreadPool , o simplemente no use QtConcurrent::run . Tener una QtConcurrent::run tarea construir QObject s que sobrevivir la tarea es un diseño cuestionable, las tareas deben ser independientes. Como señala @Mike, los QThread utilizados por QtConcurrent::run no tienen bucles de eventos.