QTimer와 이벤트 순환 및 다중 스레드
16838 단어 Qt
startTimer
은 타이머의 ID를 되돌려줍니다. 타이머 시간이 되면 QTimerEvent
을 받고 허함수 timerEvent
을 덮어쓰고 처리합니다. 이 QTimerEvent는 타이머 ID를 포함합니다.QTimer의 소스 코드를 보면 알 수 있습니다.
QObject::startTimer()
{
if (Q_UNLIKELY(!d->threadData->eventDispatcher.load())) {
qWarning("QObject::startTimer: Timers can only be used with threads started with QThread");
return 0;
}
if (Q_UNLIKELY(thread() != QThread::currentThread())) {
qWarning("QObject::startTimer: Timers cannot be started from another thread");
return 0;
}
// eventDispatcher ,killTimer unregisterTimer
int timerId = d->threadData->eventDispatcher.load()->registerTimer(interval, timerType, this);
if (!d->extraData)
d->extraData = new QObjectPrivate::ExtraData;
d->extraData->runningTimers.append(timerId);
return timerId;
}
이벤트 디스패치는 모든 QObject 대상과 관련된 타이머의 목록을 유지하고 registerTimer의 원본 코드를 보십시오:
void QEventDispatcherWin32::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object)
{
if (timerId < 1 || interval < 0 || !object) {
qWarning("QEventDispatcherWin32::registerTimer: invalid arguments");
return;
} else if (object->thread() != thread() || thread() != QThread::currentThread()) {
// event dispatcher
qWarning("QEventDispatcherWin32::registerTimer: timers cannot be started from another thread");
return;
}
Q_D(QEventDispatcherWin32);
// exiting ... do not register new timers (QCoreApplication::closingDown() is set too late to be used here)
if (d->closingDown)
return;
// ID, ,
WinTimerInfo *t = new WinTimerInfo;
t->dispatcher = this;
t->timerId = timerId;
t->interval = interval;
t->timerType = timerType;
t->obj = object;
t->inTimerEvent = false;
t->fastTimerId = 0;
if (d->internalHwnd)
d->registerTimer(t); //
d->timerVec.append(t); // store in timer vector
d->timerDict.insert(t->timerId, t); // store timers in dict
}
QAbstract Event Dispatcher::registeredTimers는 QObject 대상과 관련된 타이머의 목록을 조회하는 데 사용됩니다.즉
QList list;
으로 구성체 TimerInfo는 내연 함수를 제외하고 다음과 같이 구성됩니다.int timerId;
int interval;
Qt::TimerType timerType;
enum TimerType { //
PreciseTimer,
CoarseTimer,
VeryCoarseTimer
};
이어서 내부 실현 클래스의 동명 함수를 보면 이번에는 좀 복잡하다.
void QEventDispatcherWin32Private::registerTimer(WinTimerInfo *t)
{
// internal window handle used for socketnotifiers/timers/etc
Q_ASSERT(internalHwnd);
Q_Q(QEventDispatcherWin32);
bool ok = false;
calculateNextTimeout(t, qt_msectime());
uint interval = t->interval;
if (interval == 0u) {
// 0 timer, , QZeroTimerEvent
QCoreApplication::postEvent(q, new QZeroTimerEvent(t->timerId));
ok = true;
} else if (interval < 20u || t->timerType == Qt::PreciseTimer) {
// 20ms Precise, (fast timer)
t->fastTimerId = timeSetEvent(interval, 1, qt_fast_timer_proc, DWORD_PTR(t),
TIME_CALLBACK_FUNCTION | TIME_PERIODIC | TIME_KILL_SYNCHRONOUS );
ok = t->fastTimerId;
}
if (!ok) {
// , (Very)CoarseTimers , SetTimer
WM_TIMER QEvent::Timer QObject。
ok = SetTimer(internalHwnd, t->timerId, interval, 0);
}
if (!ok) //
qErrnoWarning("QEventDispatcherWin32::registerTimer: Failed to create a timer");
}
코드는 시간 간격에 따라 세 가지 처리 방식을 제시했다.1. 간격이 0이므로 더 이상 설명하지 않습니다.
timeSetEvent
, 리셋 함수 qt_fast_timer_proc
을 받아들입니다. 만기가 되면 리셋되고 독립된 라인에서 호출됩니다.리셋 함수post 메시지를 발송기에 보내면 발송기는 이 메시지를 얻고 QEvent::Timer
으로 보내며 원본 코드를 보십시오:void WINAPI QT_WIN_CALLBACK qt_fast_timer_proc(uint timerId, uint /*reserved*/, DWORD_PTR user, DWORD_PTR /*reserved*/, DWORD_PTR /*reserved*/)
{
if (!timerId) // sanity check
return;
WinTimerInfo *t = (WinTimerInfo*)user;
Q_ASSERT(t);
QCoreApplication::postEvent(t->dispatcher, new QTimerEvent(t->timerId));
}
qt_internal_proc
에 전송되며 QEvent::Timer로 QObject에 전송됩니다.콜백 함수의 일부 코드: else if (message == WM_TIMER) {
Q_ASSERT(d != 0);
d->sendTimerEvent(wp);
return 0;
}
다음을 찾습니다.
QEventDispatcherWin32Private::sendTimerEvent
{
WinTimerInfo *t = timerDict.value(timerId);
if (t && !t->inTimerEvent) {
// send event, but don't allow it to recurse
t->inTimerEvent = true;
QTimerEvent e(t->timerId);
// QTimerEvent
QCoreApplication::sendEvent(t->obj, &e);
. . . . . .
}
}
이상 발송된
QEvent::Timer
사건은 모두 QObject::timerEvent
에서 처리되었습니다.timerEvent()에서 timeout()
신호 전송:void QTimer::timerEvent(QTimerEvent *e)
{
if (e->timerId() == id) {
if (single)
stop();
emit timeout(QPrivateSignal());
}
}
타이머 ID
타이머 ID는 분배 알고리즘에 따라 얻어진 것으로 틀림없이 유일하다(심지어 크로스 라인에서).QObject가 한 라인에서 다른 라인으로 이동할 때 (
moveToThread
) 타이머도 함께 이동합니다.이동 타이머는 오래된 라인의 송신기에서 타이머를 간단하게 취소하고 새 라인의 송신기에 등록하는 문제입니다.타이머의 ID가 고유하지 않으면 타이머 ID가 새 스레드에 있는 기존 타이머와 충돌할 수 있습니다.moveToThread의 3대 임무 중 세 번째는 현재 라인에 있는 timer 등록을 해제하고 목표 라인에 다시 등록하는 것이다.이유는 제1조: 함수는sendEvent()를 통해
QEvent::ThreadChange
이벤트를 발송하고 QObject::이벤트에서 처리합니다.이벤트 함수의 일부 원본을 보십시오: case QEvent::ThreadChange: {
Q_D(QObject);
QThreadData *threadData = d->threadData;
QAbstractEventDispatcher *eventDispatcher = threadData->eventDispatcher.load();
if (eventDispatcher) {
QList<QAbstractEventDispatcher::TimerInfo> timers = eventDispatcher->registeredTimers(this);
if (!timers.isEmpty()) {
// do not to release our timer ids back to the pool (since the timer ids are moving to a new thread).
//
eventDispatcher->unregisterTimers(this);
QMetaObject::invokeMethod(this, "_q_reregisterTimers", Qt::QueuedConnection,
Q_ARG(void*, (new QList<QAbstractEventDispatcher::TimerInfo>(timers)))); } }
break;
}
또 함수
_q_reregisterTimers
을 비동기적으로 호출했는데 그 중에서 eventDispatcher->registerTimer(ti.timerId, ti.interval, ti.timerType, q);
은 재등록을 실현했다.다중 스레드에서 타이머 사용
위의 분석을 통해 알 수 있듯이 다중 스레드에서 타이머를 사용할 때 반드시 같은 스레드에서 타이머를 시작하고 정지해야 한다. 즉, 타이머를 만드는 스레드에서만 timeout() 신호를 받아들일 수 있다. 일반적으로 차 스레드의
run
함수이다.그렇지 않으면 startTimer와registerTimer에서 오류가 발생합니다.또 다른 방법은 타이머와 작업 종류를 모두 하위 라인으로 옮기는 것이다.지금 이 프로그램을 보면, 스레드에서 타이머를 켜고, 매초에 랜덤수를 만들고, 주 스레드의 텍스트 상자에 랜덤수를 하나씩 추가합니다.
class MyObj : public QObject
{
Q_OBJECT
public:
MyObj();
signals:
void toLine(QString line);
private slots:
void doWork();
void testTimer();
private:
QTimer* timer;
};
void MyObj::doWork()
{
qDebug()<<"timer thread:"<<:currentthread timer="<span" class="hljs-keyword">new QTimer();
connect(timer,SIGNAL(timeout()),this,SLOT(testTimer()));
timer->start(2000);
}
void MyObj::testTimer()
{
QString str = QString::number(qrand()%100);
qDebug()<<"test timer:"<
다음 라인에서timer를 만들고 열었습니다
마스터 스레드 섹션의 코드:
t = new QThread();
obj = new MyObj();
obj->moveToThread(t);
qDebug()<<"main thread:"<<QThread::currentThread();
connect(t,SIGNAL(started()), obj, SLOT(doWork()), Qt::QueuedConnection);
connect(obj,SIGNAL(toLine(QString)),this,SLOT(appendText(QString) ),Qt::QueuedConnection );
connect(t,SIGNAL(finished()), obj, SLOT(deleteLater()) );
t->start();
전편의 코드와 거의 같아서 더 이상 설명하지 않는다.
참고: Timer에 대한 Qt 공식 설명
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Qt로 문자와 이미지의 혼합 텍스트 그리기텍스트를 그리려면 QPainter::drawText 함수를 사용하지만 텍스트와 동시에 이미지 (아이콘 등)를 함께 그리기를 원합니다. QLabel와 QPushButton는 이미지와 텍스트를 표시하는 기능을 가지고 있...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.