-
Notifications
You must be signed in to change notification settings - Fork 31
Basics overview part ii: timers, threads and critical sections
NUI provides two ways for working with timers (Please note that the first timer sample code uses nuiTimer while the second one uses nglTimer):
//**************************************
// declare the timer in your class
class MyObject: public MyParent
{
[...]
private:
bool OnTick(const nuiEvent& rEvent); // timer event receiver
nuiTimer mTimer;
double mStartTime;
nuiEventSink<MyObject> mEventSink;
};
//**************************************
// init the timer in the object constructor, giving the timer period in seconds
MyObject::MyObject()
: MyParent(), mTimer(0.5f), mEventSink(this)
{
// connect the timer event to the receiver
mEventSink.Connect(mTimer.Tick, &MyObject::OnTick);
// start the timer (could be anywhere, anytime)
mStartTime = nglTime();
mTimer.Start();
}
//**************************************
// timer event receiver
bool MyObject::OnTick(const nuiEvent& rEvent)
{
// a usefull shortcut using the empty constructor of nglTime
double currentTime = nglTime();
// my ending condition
if ((currentTime - mStartTime) > 5.f)
mTimer.Stop();
// another way to use nglTime
nglTimeInfo info;
nglTime date;
date.GetLocalTime(info);
NGL_OUT(_T("::OnTick currentTime %d/%d/%d %d:%d:%d\n"),
info.Year + 1900, info.Month, info.Day, info.Hours, info.Minutes, info.Seconds);
return true;
}
class MyTimer : public nglTimer
{
[...]
private:
virtual void OnTick(nglTime Lap);
};
MyTimer::MyTimer(): nglTimer(0.5f)
{
Start(); // here or somewhere else ...
}
// overloads the OnTick method
void MyTimer::OnTick(nglTime Lap)
{
// voila!
}
As you may have guessed, nuiTimer inherits from nglTimer using this method, and add the event interface to ease development and prevent having to do too much inheritance in the application code.
class MyClass: public nglThread
{
[...]
virtual void OnStart();
bool mDone;
}
MyClass::MyClass()
: nglThread(_T("MyClass Thread")), mDone(false)
{
}
// virtual callback
void nglThread::OnStart()
{
while (!mDone)
{
// here is my threaded code
if (Condition())
mDone = true;
}
// the thread exits
}
[...]
MyClass* myObj = new MyClass();
myObj->Start(); // launch ::OnStart callback
[...]
MyClass* myObj1 = new MyClass();
MyClass* myObj2 = new MyClass();
// launch the threaded processes
myObj1->Start();
myObj2->Start();
// returns almost immediatly
// Join makes the process sleeps until the thread ends
myObj1->Join();
// same thing, for the other thread
myObj2->Join();
// this way, we are now sure that all the threads have ended, and we can now safely delete everything.
delete myObj1;
delete myObj2;
We add a message queue to our class. External world can post notifications to this message queue.
#define NOTIF_DONE _T("DONE")
class MyClass: public nglThread
{
[...]
// a method to post a message from outside
void PostMessage(nuiNotification* pNotif);
private:
// the local message queue
nuiMessageQueue mQueue;
};
void MyClass::Post(nuiNotification* notif)
{
mQueue.Post(notif);
}
// virtual callback
void MyClass:OnStart()
{
nuiNotification* pMessage;
while (!mDone)
{
// wait for a message to come
pMessage = mQueue.Get();
// if you need to, you can setup the message queue behavior:
// pMessage = mQueue.Get(100); // wait for 100ms only and then continue. If the queue was empty, the message is NULL!
// pMessage = mQueue.Get(0); // don't wait, check if a message is available and returns immediately. The message may be NULL!
// process message
if (!pMessage->GetName().Compare(NOTIF_DONE))
mDone = true;
delete pMessage;
}
}
// from another part of the application
[...]
MyClass* myObj = new MyClass();
myObj->Start();
myObj->Post(new nuiNotification(NOTIF_DONE));
myObj->Join();
Sometimes you just don’t want to create a whole class for your threaded process.
You are able to delegate the threaded process to an existing method, using the delegates system implemented in nui.
[...]
// declare the thread delegate
nglThreadDelegate delegate(nuiMakeDelegate(this, &MyWidget::MyThreadDelegate));
// and launch the thread process
delegate.Start();
[...]
// here is the delegate method for the threaded process
void MyWidget:: MyThreadDelegate()
{
while (!mDone)
{
[...]
}
}
class MyClass
{
[...]
// the c.s. is a shared object for all threads
nglCriticalSection myCS;
}
void MyClass::MyThreadedMethod()
{
myCS.Lock(); // will block until the cs is released by the previous "thread-user"
// here is the code you want to protect from multiple parallel accesses
myAttribute = false;
myCS.Unlock(); // release the cs
}
You can also go for a convenient shortcut, using a guard:
void MyClass::MyThreadedMethod()
{
// handles the lock and release functions
nglCriticalSectionGuard guard(myCS); // <=> nglGuard<nglCriticalSection> guard(myCS);
// here is the code you want to protect from multiple parallel accesses
myAttribute = false;
}
If you need to use atomics for an advanced locking system and if you know what you’re doing, you can check the nglAtomic and nglLightLock classes.