2014-01-20 3 views
3

Я написал поток в Qt, который делает много вещей (расчеты, выборка данных, ...).
Этот поток должен запускаться с интервалом в 1000 мс.
Разрешенная ошибка с таймером составляет около 5 мс.
Я изменил приоритет потока на QThread::HighPriority, но поток работает в интервале примерно до 1060 мс-1100 мс.
Как сделать интервал более точным? (Я подклассифицировал QThread и использовал msleep(interval) в методе run()).Точный интервал в QThread

+0

Но вы хотите 1000мс интервал, включающий время выполнения или 1000 мс + время выполнения. Я имею в виду 1 секунду после окончания работы резьбы или через 1 секунду после начала текущей нити? – Blood

+0

У меня есть поток, который делает много вещей в цикле (цикл находится в методе run). Этот цикл имеет интервал. Этот интервал должен выполняться точно. – Mosi

ответ

8

Вы засекретили run() метод вашего потока по существу, как:

void MyThread::run() { 
    forever { 
    doSomething(); 
    msleep(1000); 
    } 
} 

Есть несколько проблем:

  1. doSomething() не принимает нулевое количество времени. Как минимум, вам нужно будет время, которое длится doSomething(), и спать намного короче 1000 мс.

  2. Оба doSomething()иmsleep() могут принимать переменное количество времени, так как ваш поток никогда не гарантирован не будет вытеснен, а также не гарантируется сразу начать работать, как только это сделано работоспособные на сне истекающего. Таким образом, вам нужно отслеживать время абсолютно, а не относительно начала doSomething().

  3. Вы используете универсальную функцию сна, не используя при этом улучшенные API-интерфейсы, предлагаемые базовой платформой.

Разумно правильный способ пойти об этом будет выражаться с помощью этого псевдокода:

const qint64 kInterval = 1000; 
qint64 mtime = QDateTime::currentMSecsSinceEpoch(); 
forever { 
    doSomething(); 
    mtime += kInterval; 
    qint64 sleepFor = mtime - QDateTime::currentMSecsSinceEpoch(); 
    if (sleepFor < 0) { 
    // We got preempted for too long - for all we know, the system could 
    // have even gotten suspended (lid close on a laptop). 
    // Note: We should avoid the implementation-defined behavior of 
    // modulus (%) for negative values. 
    sleepFor = kInterval - ((-sleepFor) % kInterval); 
    } 
    OS_Precise_Wait_ms(sleepFor); // use the appropriate API on given platform 
} 

Как назло, Qt предоставляет API, который делает все это для вас: таймеры. Они являются источником разумного поведения периодических «тиков». Большинство наивных повторных реализаций этой функциональности, вероятно, так или иначе ошибаются, поскольку это не так просто, как кажется.

Вот как вы можете реорганизовать код:

class Worker : public QObject { 
    QBasicTimer m_timer; 
    void doSomething() { 
    // do the work 
    } 
    void timerEvent(QTimerEvent * ev) { 
    if (ev->timerId() != m_timer.timerId()) { 
     QObject::timerEvent(ev); 
     return; 
    } 
    doSomething(); 
    } 
public: 
    Worker(QObject * parent = 0) : QObject(parent) { 
    m_timer.start(1000, Qt::PreciseTimer, this); 
    } 
}; 

int main(int argc, char ** argv) { 
    QCoreApplication app(argc, argv); 
    Worker worker; 
    QThread workerThread; 
    worker.moveToThread(workerThread); 
    workerThread.start(QThread::HighPriority); 

    // Example of how to terminate the application after 10 seconds 
    // Requires Qt 5 and a C++11 compiler. 
    QTimer timer; 
    QObject::connect(&timer, &QTimer::timeout, [&](){ 
    workerThread.quit(); 
    workerThread.wait(); 
    app.quit(); 
    }); 
    timer.setTimerType(Qt::VeryCoarseTimer);   
    timer.setSingleShot(true); 
    timer.start(10000); 

    return app.exec(); 
} 
+0

Как это должно давать больше точности, если нет никаких гарантий того, как поток запланирован? На самом деле, я не думаю, что есть какая-либо безопасность, если вы не пойдете в тяжелое время в реальном времени (для чего нужна поддержка ОС). –

1

Из документов из QTimer класса:

Точность и разрешение таймера

Точность таймера зависит от используемой операционной системы и оборудования. Большинство платформ поддерживают разрешение 1 миллисекунды, хотя точность таймера не будет равна этому разрешению во многих ситуациях в реальном мире.

Точность также зависит от типа таймера. Для Qt :: PreciseTimer, QTimer попытается сохранить степень защиты в 1 миллисекунду. Точные таймеры также никогда не выходят раньше, чем ожидалось.

Для Qt :: CoarseTimer и Qt :: VeryCoarseTimer типов, QTimer может проснуться раньше, чем ожидалось, в полях для этих типов: 5% от интервала для Qt :: CoarseTimer и 500 мс для Qt :: VeryCoarseTimer.

Все типы таймеров могут истекать позже, чем ожидалось, если система занята или не может обеспечить требуемую точность. В таком случае тайм-аута overrun Qt будет выдавать активированный() только один раз, даже если несколько тайм-аутов истекли, а затем возобновит исходный интервал.

+0

Спасибо, но я не использую QTimer, вместо этого я использую QThread. – Mosi

+2

@Mosi Хотя этот ответ не самый педагогический, он подразумевает правильную вещь: вы должны использовать как таймер, так и поток! –

Смежные вопросы