web-dev-qa-db-fra.com

Les timers Qt ne peuvent pas être arrêtés depuis un autre thread

Hy,

J'écris mon premier programme Qt et j'ai maintenant des problèmes avec:

QObject :: killTimer: les timers ne peuvent pas être arrêtés depuis un autre thread

QObject :: startTimer: les timers ne peuvent pas être démarrés à partir d'un autre thread

Mon programme communiquera avec un bus CANOpen pour que j'utilise le Canfestival Stack . Le Canfestival fonctionnera avec les méthodes de rappel. Pour détecter le dépassement de délai en communication, je configure une fonction de minuterie (en quelque sorte comme un chien de garde). Mon paquet de minuterie se compose d'un module "tmr", d'un module "TimerForFWUpgrade" et d'un module "SingleTimer". Le module "tmr" était à l'origine programmé en C, donc les méthodes statiques "TimerForFWUpgrade" l'interfèrent. Le module "tmr" fera partie d'un package de mise à jour du micrologiciel programmé en C.

La minuterie fonctionnera comme suit. Avant d'envoyer un message, j'appellerai la méthode TMR_Set. Ensuite, dans ma boucle de programme inactive avec TMR_IsElapsed, nous vérifions s'il y a un dépassement de temps par minuterie. Si TMR_IsElapsed, je vais faire le traitement des erreurs. Lorsque vous voyez la méthode TMR_Set sera appelée en continu et redémarrez QTimer encore et encore.

Les erreurs mentionnées ci-dessus apparaissent si je lance mon programme. Pouvez-vous me dire si mon concept pourrait fonctionner? Pourquoi cette erreur apparaît-elle? Dois-je utiliser des threads supplémentaires (QThread) par rapport au thread principal? 

Je vous remercie

Mat

Exécuter et boucle inactive:

void run
{
    // start communicate with callbacks where TMR_Set is set continously
    ...

    while(TMR_IsElapsed(TMR_NBR_CFU) != 1);

    // if TMR_IsElapsed check for errorhandling
    ....
}  

Module tmr (interface avec le programme C): _

extern "C"
{
void TMR_Set(UINT8 tmrnbr, UINT32 time)
{
    TimerForFWUpgrade::set(tmrnbr, time);
}

INT8 TMR_IsElapsed(UINT8 tmrnbr)
{
 return TimerForFWUpgrade::isElapsed(tmrnbr);
}
}

Module TimerForFWUpgrade:

SingleTimer* TimerForFWUpgrade::singleTimer[NR_OF_TIMERS];

TimerForFWUpgrade::TimerForFWUpgrade(QObject* parent)
{

    for(unsigned char i = 0; i < NR_OF_TIMERS; i++)
    {
        singleTimer[i] = new SingleTimer(parent);
    }
}

//static
void TimerForFWUpgrade::set(unsigned char tmrnbr, unsigned int time)
{
    if(tmrnbr < NR_OF_TIMERS)
    {
        time *= TimerForFWUpgrade::timeBase;
        singleTimer[tmrnbr]->set(time);
    }

}


//static
char TimerForFWUpgrade::isElapsed(unsigned char tmrnbr)
{
    if(true == singleTimer[tmrnbr]->isElapsed())
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

Module SingleTimer:

SingleTimer::SingleTimer(QObject* parent) : QObject(parent),
                                            pTime(new QTimer(this)),
                                            myElapsed(true)
{
    connect(pTime, SIGNAL(timeout()), this, SLOT(slot_setElapsed()));
    pTime->setTimerType(Qt::PreciseTimer);
    pTime->setSingleShot(true);
}

void SingleTimer::set(unsigned int time)
{
    myElapsed = false;
    pTime->start(time);
}

bool SingleTimer::isElapsed()
{
    QCoreApplication::processEvents();
    return myElapsed;
}

void SingleTimer::slot_setElapsed()
{
    myElapsed = true;
}
11
Matt

Vous avez ce problème parce que les minuteries du tableau statique sont créées dans Thread X, mais démarrées et arrêtées dans Thread Y. Ceci n'est pas autorisé, car Qt s'appuie sur l'affinité des threads pour les temporisations.

Vous pouvez créer, commencer à arrêter dans le même thread ou utiliser signal et slots pour déclencher des opérations start et stop pour des minuteries. La solution du signal et de la fente pose quelques problèmes car vous avez des objets n QTimer (indice: comment démarrer le chronomètre à la position i?)

Ce que vous pouvez faire à la place est de créer et d’initialiser le temporisateur à la position tmrnbr dans 

TimerForFWUpgrade::set(unsigned char tmrnbr, unsigned int time)
{
     singleTimer[tmrnbr] = new SingleTimer(0);
     singleTimer[tmrnbr]->set(time);
}

qui est exécuté par le même fil.

De plus, vous n'avez pas besoin d'une classe SingleTimer. Vous utilisez Qt5 et vous avez déjà tout ce dont vous avez besoin à votre disposition:

  • SingleTimer::isElapsed est vraiment QTimer::remainingTime() == 0;
  • SingleTimer::set est vraiment QTimer::setSingleShot(true); QTimer::start(time); 
  • SingleTimer::slot_setElapsed devient inutile 
  • ThusSingleTimer::SingleTimer devient inutile et vous n'avez plus besoin d'une classe SingleTimer
6
UmNyobe

Utilisez QTimer à cette fin et utilisez SIGNALS et SLOT pour démarrer et arrêter le ou les temporisateurs à partir de différents threads. Vous pouvez émettre le signal de n'importe quel thread et l'attraper dans le thread qui a créé le timer pour agir dessus.

Puisque vous dites que vous êtes nouveau sur Qt, je vous suggère de suivre quelques tutoriels avant de continuer, afin que vous sachiez ce que Qt a à offrir et que vous n'essayiez pas de réinventer la roue. :)

VoidRealms est un bon point de départ.

6
nnb

J'ai éliminé les erreurs après avoir changé le concept de ma minuterie. Je n'utiliserais plus mon module SingleTimer. Avant QTimer, je ne laisserai pas le temps mort et peut-être à cause de cela, j'ai des problèmes. Maintenant, j'ai une minuterie cyclique qui expire toutes les 100 ms dans la fonction slot et je compte ensuite les événements. En dessous de mon code de travail:

TimerForFWUpgrade::TimerForFWUpgrade(QObject* parent) : QObject(parent),
                                                        pTime(new QTimer(this))
{
    connect(pTime, SIGNAL(timeout()), this, SLOT(slot_handleTimer()));
    pTime->setTimerType(Qt::PreciseTimer);
    pTime->start(100);
}

void TimerForFWUpgrade::set(unsigned char tmrnbr, unsigned int time)
{
    if(tmrnbr < NR_OF_TIMERS)
    {
        if(timeBase != 0)
        {
            myTimeout[tmrnbr] = time / timeBase;
        }
        else
        {
            myTimeout[tmrnbr] = 0;
        }
        myTimer[tmrnbr] = 0;
        myElapsed[tmrnbr] = false;
        myActive[tmrnbr] = true;
    }

}

char TimerForFWUpgrade::isElapsed(unsigned char tmrnbr)
{
    QCoreApplication::processEvents();
    if(tmrnbr < NR_OF_TIMERS)
    {
        if(true == myElapsed[tmrnbr])
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
    else
    {
        return 0; // NOK
    }
}

void TimerForFWUpgrade::slot_handleTimer()
{
    for(UINT8 i = 0; i < NR_OF_TIMERS; i++)
    {
        if(myActive[i] == true)
        {
            myTimer[i]++;
            if(myTimeout[i] < myTimer[i])
            {
                myTimer[i] = 0;
                myElapsed[i] = true;
                myActive[i] = false;
            }
         }
    }
}
0
Matt