En primer lugar, probablemente debería no poner ninguna ranura en QThreads. QThreads no están destinados a ser derivados de otro modo que la reimplementación del método run
y métodos privados (¡no señales!).
Un QThread es conceptualmente un controlador de subprocesos, no un subproceso en sí mismo. En la mayoría de los casos, debe tratar con QObjects. Comience un hilo, luego mueva la instancia del objeto a ese hilo. Esa es la única manera en que conseguirás que las máquinas tragamonedas funcionen correctamente en el hilo. Mover la instancia de subproceso (es QObject-derived!) Al subproceso es un truco y un estilo incorrecto. No hagas eso a pesar de las publicaciones desinformadas en el foro que dicen lo contrario.
En cuanto al resto de su pregunta: una llamada de ranura de señal no tiene que encontrar nada ni validar mucho. La "ubicación" y la "validación" se realizan cuando se establece la conexión. Los principales pasos realizados en el momento de la llamada son:
Bloqueo de un mutex de ranura de señal de un grupo.
Iteración a través de la lista de conexiones.
Realización de llamadas utilizando llamadas directas o en cola.
de costes comunes
Cualquier llamada ranura señal siempre comienza como una llamada directa en la aplicación de la señal generada por moc. Se construye una matriz de punteros a argumentos de la señal en la pila. Los argumentos no se copian
La señal llama al QMetaObject::activate
, donde se adquiere la lista de conexiones mutex, y se itera la lista de ranuras conectadas, colocando la llamada para cada ranura.
conexiones directas
No hay mucho que se hace allí, la ranura se llama ya sea llamando directamente QObject::qt_static_metacall
obtenido en el momento de establecerse la conexión, o si el QObject::qt_metacall
QMetaObject::connect
se utilizó para configurar la conexión. Este último permite dynamic creation of signals and slots.
conexiones en cola
Los argumentos que marshalled y copiado, ya que la llamada tiene que ser almacenado en una cola de eventos y la señal debe devolver. Esto se hace asignando una matriz de punteros a copias, y copiando consting cada argumento en el montón. El código para hacer eso es realmente sin adornos antiguo C.
La puesta en cola de la llamada se realiza dentro de queued_activate
. Aquí es donde se realiza la copia de construcción.
La sobrecarga de una llamada en cola es siempre al menos una asignación de montón de QMetaCallEvent
. Si la llamada tiene algún argumento, se asigna una matriz de punteros a argumentos y se realiza una asignación adicional para cada argumento. Para una llamada con argumentos n
, el costo dado como una expresión C es (n ? 2+n : 1)
asignaciones. Un valor de retorno para bloquear llamadas es contador como argumento. Podría decirse que este aspecto de Qt podría optimizarse hasta una asignación para todo, pero en la vida real solo importaría si está llamando a métodos triviales.
Resultados de referencia
Incluso una (no en cola) llamada ranura señal directa tiene una sobrecarga medible, pero hay que elegir sus batallas. Facilidad para diseñar el código en comparación con el rendimiento. Mide el rendimiento de su aplicación final e identifica los cuellos de botella, ¿verdad? Si lo haces, es probable que veas que en las aplicaciones de la vida real, los gastos generales de la ranura de la señal no juegan ningún papel.
El único mecanismo de ranura de señal de tiempo tiene una sobrecarga significativa si está llamando a funciones triviales. Supongamos que llama al trivial
en el siguiente código. Es un punto de referencia completo e independiente, así que siéntete libre de ejecutarlo y verlo por ti mismo. Los resultados en mi máquina fueron:
Warming up the caches...
trivial direct call took 3ms
nonTrivial direct call took 376ms
trivial direct signal-slot call took 158ms, 5166% longer than direct call.
nonTrivial direct signal-slot call took 548ms, 45% longer than direct call.
trivial queued signal-slot call took 2474ms, 1465% longer than direct signal-slot and 82366% longer than direct call.
nonTrivial queued signal-slot call took 2474ms, 416% longer than direct signal-slot and 653% longer than direct call.
Lo que debe tenerse en cuenta, tal vez, es que la concatenación de cadenas es bastante rápido :)
Tenga en cuenta que estoy haciendo las llamadas a través de un puntero de función, esta es evitar que el compilador optimice las llamadas directas a la función de adición.
//main.cpp
#include <cstdio>
#include <QCoreApplication>
#include <QObject>
#include <QTimer>
#include <QElapsedTimer>
#include <QTextStream>
static const int n = 1000000;
class Test : public QObject
{
Q_OBJECT
public slots:
void trivial(int*, int, int);
void nonTrivial(QString*, const QString&, const QString&);
signals:
void trivialSignalD(int*, int, int);
void nonTrivialSignalD(QString*, const QString&, const QString &);
void trivialSignalQ(int*, int, int);
void nonTrivialSignalQ(QString*, const QString&, const QString &);
private slots:
void run();
private:
void benchmark(bool timed);
void testTrivial(void (Test::*)(int*,int,int));
void testNonTrivial(void (Test::*)(QString*,const QString&, const QString&));
public:
Test();
};
Test::Test()
{
connect(this, SIGNAL(trivialSignalD(int*,int,int)),
SLOT(trivial(int*,int,int)), Qt::DirectConnection);
connect(this, SIGNAL(nonTrivialSignalD(QString*,QString,QString)),
SLOT(nonTrivial(QString*,QString,QString)), Qt::DirectConnection);
connect(this, SIGNAL(trivialSignalQ(int*,int,int)),
SLOT(trivial(int*,int,int)), Qt::QueuedConnection);
connect(this, SIGNAL(nonTrivialSignalQ(QString*,QString,QString)),
SLOT(nonTrivial(QString*,QString,QString)), Qt::QueuedConnection);
QTimer::singleShot(100, this, SLOT(run()));
}
void Test::run()
{
// warm up the caches
benchmark(false);
// do the benchmark
benchmark(true);
}
void Test::trivial(int * c, int a, int b)
{
*c = a + b;
}
void Test::nonTrivial(QString * c, const QString & a, const QString & b)
{
*c = a + b;
}
void Test::testTrivial(void (Test::* method)(int*,int,int))
{
static int c;
int a = 1, b = 2;
for (int i = 0; i < n; ++i) {
(this->*method)(&c, a, b);
}
}
void Test::testNonTrivial(void (Test::* method)(QString*, const QString&, const QString&))
{
static QString c;
QString a(500, 'a');
QString b(500, 'b');
for (int i = 0; i < n; ++i) {
(this->*method)(&c, a, b);
}
}
static int pct(int a, int b)
{
return (100.0*a/b) - 100.0;
}
void Test::benchmark(bool timed)
{
const QEventLoop::ProcessEventsFlags evFlags =
QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers;
QTextStream out(stdout);
QElapsedTimer timer;
quint64 t, nt, td, ntd, ts, nts;
if (!timed) out << "Warming up the caches..." << endl;
timer.start();
testTrivial(&Test::trivial);
t = timer.elapsed();
if (timed) out << "trivial direct call took " << t << "ms" << endl;
timer.start();
testNonTrivial(&Test::nonTrivial);
nt = timer.elapsed();
if (timed) out << "nonTrivial direct call took " << nt << "ms" << endl;
QCoreApplication::processEvents(evFlags);
timer.start();
testTrivial(&Test::trivialSignalD);
QCoreApplication::processEvents(evFlags);
td = timer.elapsed();
if (timed) {
out << "trivial direct signal-slot call took " << td << "ms, "
<< pct(td, t) << "% longer than direct call." << endl;
}
timer.start();
testNonTrivial(&Test::nonTrivialSignalD);
QCoreApplication::processEvents(evFlags);
ntd = timer.elapsed();
if (timed) {
out << "nonTrivial direct signal-slot call took " << ntd << "ms, "
<< pct(ntd, nt) << "% longer than direct call." << endl;
}
timer.start();
testTrivial(&Test::trivialSignalQ);
QCoreApplication::processEvents(evFlags);
ts = timer.elapsed();
if (timed) {
out << "trivial queued signal-slot call took " << ts << "ms, "
<< pct(ts, td) << "% longer than direct signal-slot and "
<< pct(ts, t) << "% longer than direct call." << endl;
}
timer.start();
testNonTrivial(&Test::nonTrivialSignalQ);
QCoreApplication::processEvents(evFlags);
nts = timer.elapsed();
if (timed) {
out << "nonTrivial queued signal-slot call took " << ts << "ms, "
<< pct(nts, ntd) << "% longer than direct signal-slot and "
<< pct(nts, nt) << "% longer than direct call." << endl;
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Test t;
return a.exec();
}
#include "main.moc"
Sugiero que veas el tutorial muy claro sobre cómo usar definitivamente QThread http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the- full-explanation/ – linello
Ella dice que es más o menos lo que digo aquí ... –
Hay un error en el código anterior, la última prueba imprime el tiempo transcurrido (ts) de la prueba anterior. – Richy