juce中timer总体说还是比较好用的,使用时只需继承timer类, 重写callback然后调用start就可以了,juce的timer比较特别,自己通过线程实现,starttimer的时候会创建一个timerthread,然后在这个timerthread的构造函数中又触发了triggerAsyncUpdate(); 这样异步使线程start, 这样做是为了让timer对象添登记以后才开始线程,这也是常用技法, 然后在线程中检查定时是否到达,如果到达了的话就post消息,然后回调,只是这时其实已经在消息线程中了
struct CallTimersMessage : public MessageManager::MessageBase
{
CallTimersMessage() {} void messageCallback() override
{
if (instance != nullptr)
instance->callTimers();
}
};
instance->callTimers(); 再调用各个timer的callback,也就是你重写的处理函数,大体就分析到这里,下次再进行一次仔细分析,前提条件没有准备充分,仔细分析太耗费时间。
/*
============================================================================== This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information. ==============================================================================
*/ class Timer::TimerThread : private Thread,
private DeletedAtShutdown,
private AsyncUpdater
{
public:
typedef CriticalSection LockType; // (mysteriously, using a SpinLock here causes problems on some XP machines..) TimerThread()
: Thread ("Juce Timer"),
firstTimer (nullptr)
{
triggerAsyncUpdate();
} ~TimerThread() noexcept
{
signalThreadShouldExit();
callbackArrived.signal();
stopThread (4000); jassert (instance == this || instance == nullptr);
if (instance == this)
instance = nullptr;
} void run() override
{
uint32 lastTime = Time::getMillisecondCounter();
MessageManager::MessageBase::Ptr messageToSend (new CallTimersMessage()); while (! threadShouldExit())
{
const uint32 now = Time::getMillisecondCounter(); const int elapsed = (int) (now >= lastTime ? (now - lastTime)
: (std::numeric_limits<uint32>::max() - (lastTime - now)));
lastTime = now; const int timeUntilFirstTimer = getTimeUntilFirstTimer (elapsed); if (timeUntilFirstTimer <= 0)
{
if (callbackArrived.wait (0))
{
// already a message in flight - do nothing..
}
else
{
messageToSend->post(); if (! callbackArrived.wait (300))
{
// Sometimes our message can get discarded by the OS (e.g. when running as an RTAS
// when the app has a modal loop), so this is how long to wait before assuming the
// message has been lost and trying again.
messageToSend->post();
} continue;
}
} // don't wait for too long because running this loop also helps keep the
// Time::getApproximateMillisecondTimer value stay up-to-date
wait (jlimit (1, 100, timeUntilFirstTimer));
}
} void callTimers()
{
// avoid getting stuck in a loop if a timer callback repeatedly takes too long
const uint32 timeout = Time::getMillisecondCounter() + 100; const LockType::ScopedLockType sl (lock); while (firstTimer != nullptr && firstTimer->timerCountdownMs <= 0)
{
Timer* const t = firstTimer;
t->timerCountdownMs = t->timerPeriodMs; removeTimer (t);
addTimer (t); const LockType::ScopedUnlockType ul (lock); JUCE_TRY
{
t->timerCallback();
}
JUCE_CATCH_EXCEPTION if (Time::getMillisecondCounter() > timeout)
break;
} callbackArrived.signal();
} void callTimersSynchronously()
{
if (! isThreadRunning())
{
// (This is relied on by some plugins in cases where the MM has
// had to restart and the async callback never started)
cancelPendingUpdate();
triggerAsyncUpdate();
} callTimers();
} static inline void add (Timer* const tim) noexcept
{
if (instance == nullptr)
instance = new TimerThread(); instance->addTimer (tim);
} static inline void remove (Timer* const tim) noexcept
{
if (instance != nullptr)
instance->removeTimer (tim);
} static inline void resetCounter (Timer* const tim, const int newCounter) noexcept
{
if (instance != nullptr)
{
tim->timerCountdownMs = newCounter;
tim->timerPeriodMs = newCounter; if ((tim->nextTimer != nullptr && tim->nextTimer->timerCountdownMs < tim->timerCountdownMs)
|| (tim->previousTimer != nullptr && tim->previousTimer->timerCountdownMs > tim->timerCountdownMs))
{
instance->removeTimer (tim);
instance->addTimer (tim);
}
}
} static TimerThread* instance;
static LockType lock; private:
Timer* volatile firstTimer;
WaitableEvent callbackArrived; struct CallTimersMessage : public MessageManager::MessageBase
{
CallTimersMessage() {} void messageCallback() override
{
if (instance != nullptr)
instance->callTimers();
}
}; //==============================================================================
void addTimer (Timer* const t) noexcept
{
#if JUCE_DEBUG
// trying to add a timer that's already here - shouldn't get to this point,
// so if you get this assertion, let me know!
jassert (! timerExists (t));
#endif Timer* i = firstTimer; if (i == nullptr || i->timerCountdownMs > t->timerCountdownMs)
{
t->nextTimer = firstTimer;
firstTimer = t;
}
else
{
while (i->nextTimer != nullptr && i->nextTimer->timerCountdownMs <= t->timerCountdownMs)
i = i->nextTimer; jassert (i != nullptr); t->nextTimer = i->nextTimer;
t->previousTimer = i;
i->nextTimer = t;
} if (t->nextTimer != nullptr)
t->nextTimer->previousTimer = t; jassert ((t->nextTimer == nullptr || t->nextTimer->timerCountdownMs >= t->timerCountdownMs)
&& (t->previousTimer == nullptr || t->previousTimer->timerCountdownMs <= t->timerCountdownMs)); notify();
} void removeTimer (Timer* const t) noexcept
{
#if JUCE_DEBUG
// trying to remove a timer that's not here - shouldn't get to this point,
// so if you get this assertion, let me know!
jassert (timerExists (t));
#endif if (t->previousTimer != nullptr)
{
jassert (firstTimer != t);
t->previousTimer->nextTimer = t->nextTimer;
}
else
{
jassert (firstTimer == t);
firstTimer = t->nextTimer;
} if (t->nextTimer != nullptr)
t->nextTimer->previousTimer = t->previousTimer; t->nextTimer = nullptr;
t->previousTimer = nullptr;
} int getTimeUntilFirstTimer (const int numMillisecsElapsed) const
{
const LockType::ScopedLockType sl (lock); for (Timer* t = firstTimer; t != nullptr; t = t->nextTimer)
t->timerCountdownMs -= numMillisecsElapsed; return firstTimer != nullptr ? firstTimer->timerCountdownMs : 1000;
} void handleAsyncUpdate() override
{
startThread (7);
} #if JUCE_DEBUG
bool timerExists (Timer* const t) const noexcept
{
for (Timer* tt = firstTimer; tt != nullptr; tt = tt->nextTimer)
if (tt == t)
return true; return false;
}
#endif JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimerThread)
}; Timer::TimerThread* Timer::TimerThread::instance = nullptr;
Timer::TimerThread::LockType Timer::TimerThread::lock; //==============================================================================
Timer::Timer() noexcept
: timerCountdownMs (0),
timerPeriodMs (0),
previousTimer (nullptr),
nextTimer (nullptr)
{
} Timer::Timer (const Timer&) noexcept
: timerCountdownMs (0),
timerPeriodMs (0),
previousTimer (nullptr),
nextTimer (nullptr)
{
} Timer::~Timer()
{
stopTimer();
} void Timer::startTimer (const int interval) noexcept
{
// If you're calling this before (or after) the MessageManager is
// running, then you're not going to get any timer callbacks!
jassert (MessageManager::getInstanceWithoutCreating() != nullptr); const TimerThread::LockType::ScopedLockType sl (TimerThread::lock); if (timerPeriodMs == 0)
{
timerCountdownMs = interval;
timerPeriodMs = jmax (1, interval);
TimerThread::add (this);
}
else
{
TimerThread::resetCounter (this, interval);
}
} void Timer::startTimerHz (int timerFrequencyHz) noexcept
{
if (timerFrequencyHz > 0)
startTimer (1000 / timerFrequencyHz);
else
stopTimer();
} void Timer::stopTimer() noexcept
{
const TimerThread::LockType::ScopedLockType sl (TimerThread::lock); if (timerPeriodMs > 0)
{
TimerThread::remove (this);
timerPeriodMs = 0;
}
} void JUCE_CALLTYPE Timer::callPendingTimersSynchronously()
{
if (TimerThread::instance != nullptr)
TimerThread::instance->callTimersSynchronously();
}