Introduction

 

We will post a blog series to introduce multi-threading programming in Windows Store app. In this blog series, we will walk through a sample together to demonstrate which multi-threading APIs can be used, how to create a thread, and synchronization.

As the second part in this series, this article will introduce how to substitute the traditional while and sleep thread function model in Windows Runtime.

Keep moving the ball

 

In the first step, we use threading to operate the movement of the ball. The next step we need to consider is how to make the ball keep moving.

One thought is to use a While loop to always call the function of ball movement. However, we need a Sleep function to wait as the While loop executes too fast.

In the Windows Store Apps, the Sleep function is not available any more. No matter in C++ or .Net environment, although std::thread and std::this_thread::sleep_for  are  compliable, they will crash in runtime,  Windows Store Apps adopts the Non-blocking mode, hence,  the Sleep or other similar APIs  are not allowed.

How to resolve this problem?  Two classes Windows::UI::Xaml::DispatcherTimer and Windows::System::Threading::ThreadPoolTimer introduced in Windows Store apps overcome this problem.

 

The following sample demonstrates how to implement the Sleep function with them.

 

void MainPage::Usedispatchertimer()

{

            Windows::UI::Xaml::DispatcherTimer^ tempdispatchertime=ref new DispatcherTimer();

            Windows::Foundation::TimeSpan time;

            time.Duration = 10000;

            tempdispatchertime->Interval=time;

            auto timerDelegate = [this](Object^ e,Object^ ags)

            { 

                        auto uiDelegate = [this]()

                        {

                                    double x=Canvas::GetLeft(round1);

                                    Canvas::SetLeft(round1,x-3);

 

                        };

 

                        Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal,

                                    ref new Windows::UI::Core::DispatchedHandler(uiDelegate));

            };

            tempdispatchertime->Tick+=ref new EventHandler<Object^>(timerDelegate);

            tempdispatchertime->Start(); 

}

 

In above code, tempdispatchertime->Interval sets the interval with 100ns for the unit. The Handler of DispatcherTimer differs from ThreadPool::RunAsync, so the input parameters of Lambda expression change to Object^ e,Object^ ags. 

tempdispatchertime->Tick is used to register handle, of course, the handle can be unregistered as well, we will discuss it later.  tempdispatchertime->Start() starts the execution. 

Thus, the code inside Lambda expression will be repeat executed based on the time interval, which generates a similar effect with while loop and Sleep function. 

After execution, we see the ball keeps moving to the left, even out of bound.

 

Using Event and Delegate

 

Next, we let the ball moves inside the bound.  In the last article, we design a method of one-way direction movement. We can make the ball move to another direction with the same mechanism.

Boundary detection will be given during the movement; we call the method of moving to the opposite direction if the ball moves out of bound. Then how to invoke that method? We use event to do that.

 Again, we add another ball. 

Now we only consider the horizontal movement, and we use event to trigger the methods of different direction movement.

 

First, we need to declare a delegate as follows.

public delegate void MoveElement(Windows::UI::Xaml::UIElement^ shapes,int index); 

Two events

event MoveElement^ AgainstLeft;

event MoveElement^ AgainstRight;

Methods for moving towards different direction need to match the delegate declaration

void Moveleft(Windows::UI::Xaml::UIElement^ shapes,int index);

void Moveright(Windows::UI::Xaml::UIElement^ shapes,int index);

 

One DispatcherTimer array

Platform::Array<Windows::UI::Xaml::DispatcherTimer^>^ dispatcherarr;

 

Two Windows::Foundation::EventRegistrationToken cookierarr[THREADNUM]; we will discuss its usage in later section.

In the initialize function of MainPage, we initialize the DispatcherTimer array, and register the events of moving left and right.

AgainstLeft+=ref new MoveElement(this,&MainPage::Moveright);

AgainstRight+=ref new MoveElement(this,&MainPage::Moveleft);

dispatcherarr=ref new Platform::Array<Windows::UI::Xaml::DispatcherTimer^>(THREADNUM);

 

Below code is for moving left and right:

void MainPage::Moveleft(Windows::UI::Xaml::UIElement^ shapes,int index)

{

            Windows::UI::Xaml::DispatcherTimer^ dispatchertimer=dispatcherarr->get(index);

            auto runwithTimer = [this,shapes,index](Object^ e,Object^ ags)

            {

                        auto uiDelegate = [this,shapes,index]()

                        {

                                    double x=Canvas::GetLeft(shapes);

                                    // WaitForSingleObjectEx use for hitting test. You can delete this in the first sample

                                    if(x<0||(WAIT_OBJECT_0==WaitForSingleObjectEx(ghSemaphore,1,TRUE)))

                                    {

                                                Windows::UI::Xaml::DispatcherTimer^ dispatchertimer=dispatcherarr->get(index);

                                                dispatchertimer->Tick-=cookierarr[index];

                                                AgainstLeft(shapes,index);

                                                return;

                                    }

                                    Canvas::SetLeft(shapes,x-5);

                        };

 

                        Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal,

                                    ref new Windows::UI::Core::DispatchedHandler(uiDelegate));

            };

 

            cookierarr[index] = dispatchertimer->Tick+=ref new EventHandler<Object^>(runwithTimer); 

}

 

void MainPage::Moveright(Windows::UI::Xaml::UIElement^ shapes,int index)

{

            Windows::UI::Xaml::DispatcherTimer^ dispatchertimer=dispatcherarr->get(index);

            auto runwithTimer = [this,shapes,index](Object^ e,Object^ ags)

            {

                        auto uiDelegate = [this,shapes,index]()

                        {

                                    double x=Canvas::GetLeft(shapes);

                                    FrameworkElement^ eshape1=safe_cast<FrameworkElement^>(shapes);

                                    // WaitForSingleObjectEx use for hitting test. You can delete this in the first sample

 

                                    if(x+eshape1->Width>this->canvas->ActualWidth||(WAIT_OBJECT_0==WaitForSingleObjectEx(ghSemaphore,1,TRUE)))

                                    {

                                                Windows::UI::Xaml::DispatcherTimer^ dispatchertimer=dispatcherarr->get(index);

                                                dispatchertimer->Tick-=cookierarr[index];

                                                AgainstRight(shapes,index);

                                                return;

                                    }

                                    Canvas::SetLeft(shapes,x+5);

                        };

 

                        Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal,

                                    ref new Windows::UI::Core::DispatchedHandler(uiDelegate));

            };

 

            cookierarr[index] = dispatchertimer->Tick+=ref new EventHandler<Object^>(runwithTimer); 

 

The logical of these two methods is trigger the opposite movement event when the ball is going out of bound.

dispatchertimer->Tick-=cookierarr[index];

This line is to remove cookie which is the return value of dispatchertimer->Tick+=ref new EventHandler<Object^>(runwithTimer);

When we register the handle of dispatchertimer, this cookie will be returned, when this method is not in use, we will unregister this cookie. Unless the dispatchertimer stops, the cookie method gets executed.

 

If we do not unregister this cookie, the both exists of moving left and right methods or methods of the same direction in dispatchertimer at the same time will result in the stop or twice the normal rate movement of the ball.

 

Here we write a method to call the move left/right methods.

void MainPage::Usetwodispatchertimer()

            Windows::UI::Xaml::DispatcherTimer^ tempdispatchertime;

            Windows::Foundation::TimeSpan time;

            time.Duration =10000;

            // Moving the first ball

            if(dispatcherarr->get(0)==nullptr)

            {

                        tempdispatchertime=ref new DispatcherTimer();

                        tempdispatchertime->Interval=time;

                        dispatcherarr->set(0,tempdispatchertime);

                        Moveleft(this->round1,0);

            }

            dispatcherarr->get(0)->Start();

            // Moving the second ball

            if(dispatcherarr->get(1)==nullptr)

            {

                        tempdispatchertime=ref new DispatcherTimer();

                        tempdispatchertime->Interval=time;

                        dispatcherarr->set(1,tempdispatchertime);

                        Moveright(this->round2,1);

            }

            dispatcherarr->get(1)->Start();

}

 

After the compilation and execution, we see there are two balls move inside either side of boundaries.

If you would like the ball stops, then add a Button and have below code inside it.

 

for(int i=0;i<THREADNUM;i++)

            {

                        if(dispatcherarr->get(i)!=nullptr)

                        {

                                    dispatcherarr->get(i)->Stop(); 

                        }

            }

 

Using semaphore

 

Then, we will discuss the collision detection of the ball. There are many approaches for collision detection, here we demonstrates how to use semaphore to implement the movement of opposite direction after collision.

 

We wait for the semaphore in the code of ball collision detection. Once this semaphore is triggered, we let the ball move towards an opposite direction.

 

Then, launch a thread to monitor the distance between two balls, and release two semaphores.

Create a new semaphore

               ghSemaphore=CreateSemaphoreEx( NULL,           // default security attributes

                        0,  // initial count

                        4,  // maximum count

                        L"mysemaphore",0,SYNCHRONIZE|SEMAPHORE_MODIFY_STATE);  

 

Below is the code of monitoring thread.

void MainPage::Watchthread(Windows::UI::Xaml::UIElement^ shape1,Windows::UI::Xaml::UIElement^ shape2,int index)

{

            Windows::UI::Xaml::DispatcherTimer^ dispatchertimer=dispatcherarr->get(index);

            auto runwithTimer = [this,shape1,shape2,index](Object^ e,Object^ ags)

            {

                        Windows::Foundation::TimeSpan time;

                        time.Duration =10000;

                        dispatcherarr->get(index)->Interval=time;

                        double shape1left=Canvas::GetLeft(shape1);

                        double shape2left=Canvas::GetLeft(shape2);

                        FrameworkElement^ eshape1=safe_cast<FrameworkElement^>(shape1);

                        double width=eshape1->Width;

                        double dis=shape1left>shape2left?(shape1left-shape2left):(shape2left-shape1left);

                        if(dis<width)

                        {

                                    BOOL re=ReleaseSemaphore(ghSemaphore,2,NULL);

                                   

                                    time.Duration =1000000;

                                    dispatcherarr->get(index)->Interval=time;

                                    //dispatcherarr->get(index)->Start();

                        }

                        return;

            };

 

            cookierarr[index] = dispatchertimer->Tick+=ref new EventHandler<Object^>(runwithTimer);

}

 

time.Duration which is the execution frequency of monitoring thread could arise a problem. If the monitoring thread still follow the previous frequency after ball collision, a possibility is monitoring thread consider the ball collide again even if the ball has not yet separated.  Consequently, we need to slow down the execution frequency of monitoring thread, and a normal way is to create a Sleep method which is unavailable in Windows Store Apps.  An approach I use is to modify the value of time.Duration, let it has enough time to wait until the balls are separated, and then recover it to the default value when the monitoring thread gets executed next time.  Another approach would be modifying the collision distance, that is, modify the position of balls after collision, and let the distance between them is greater than the sum of their radius. Here we adopt the former.

 

The above code only implement the collision and boundaries detection of the ball in the horizontal direction,  and the semaphore may apply  to only two balls, as it  may not be able to have one-to-one association with ball. Suppose there are three balls, we detect the collision, and release two semaphores, so the one who receives semaphore may not be the ball who is going to collide.

  

At the last, some samples have been attached for your reference. Hope you can provide some suggestions in the development of Windows Store Apps.

https://skydrive.live.com/redir?resid=6C98BB8F525FA790!175