views:

994

answers:

3

With reference to this programming game I am currently building.

I have a Class Library (dll) that will have a method Run which will be composed of something like such:

public class MyRobot : Robot 
{
    public void Run(} 
    {
        while (true) 
        {
           Ahead(200); //moves the bot 200pixels
           TurnLeft(90); //turns the bot by 90deg 
        }
    }
}

In those methods (inherited from Robot), the system will animate the robot using WPF (using BeginAnimation or the DispatcherTimer).

Now, the problem is that I don't a method to return (ie, move on to the next method) before completing the current one, because that will result in the animations taking place together, and when in an infinite loop (like the one above), that's especially not good.


My question is, what is the best way to prevent a method from returning before completing the animation ?

I currently have a bool in the Robot class (isActionRunning) that be flagged to true when an action starts running and then changes to false in an animation callback (using the Completed event if using BeginAnimation).

At the end of each method (after invoking BeginAnimation) I placed the following loop :

while (isActionRunning) 
{
    Thread.Sleep(200); //so that the thread sleeps for 200ms and then checks again if the animation is still running 
}

This is so that the method won't return before the animation finishes.

But I feel that this is not the right way to do this.

Can anyone guide me to what's best to achieve this ?

A: 

I would do something similar to what you described. I recently worked on something where I called a bunch of asynchronous operations, and I wanted to wait for them all to complete before calling another operation. I created a collection to represent all of the operations currently executing and instead of adding items to the collection directly, I created methods for adding and removing. The remove method had logic that said "lock the collection, remove the key provided, and if empty call the next operation". You could create a similar mechanism like this where a collection scoped at the application or window keeps track of operations being executed. You add a key to the collection and pass this key to your async operation. When the async operation completes, lock the collection and remove the key. Then you can have a loop that blocks your method from returning while the collection contains the key (with optional logic for adding a timeout or any other additional ways where you might want the method to be able to return and not be blocked forever).

This might be overkill, but I have limited experience with this issue myself. For all I know, .Net might even have a canned solution to this problem that works with one or two lines of code.

Rich
+2  A: 

Here's one option, it will only work if the Robot.Run code is running in a different thread than the UI thread doing the animations.

In the Robot.Ahead method (for example), use Dispatcher.Invoke (not BeginInvoke) to call the method that starts the animation, than add a lock on the robot with an empty block( lock(this) { } ).

In the method that starts the animation call Monitor.Enter(robot) before starting the animation

In the animation complete handler call Monitor.Leave(robot)

The result will be

time      robot thread          UI thread  
  |       ---------------       --------------  
  |       call Invoke    --->  
  |                             lock robot (Monitor.Enter)  
  |                             begin animation  
  |       Invoke returns <---   return  
  |       lock(this)            (animation running)  
  |       (wait for lock  
  |       to become available)  
  |  
  |                             Animation complete  
  |                             release lock (Monitor.Exit)  
  |       (lock available,  
  |       continue running)  
  |       Release lock (exit lock block)  
  |       Return and start next  
  |       movement  
  \/
Nir
Excellent, this is great. I have now changed my logic to implement what you suggested, instead of the while(isActionRunning) loop.
Andreas Grech
This is *crazy*. If you want to wait for one thread to signal another that its current work is done, don't build a signal out of locks, **just use a signal**. There already is an object that does this in the framework; just use it. http://msdn.microsoft.com/en-us/library/system.threading.autoresetevent.aspx
Eric Lippert
@Eric I don't remember why I used locks (I wrote that a year ago after all), It's possible I had a good reason - but I can't think of any so signals are probably the right solution here.However, in my defense, from my experience, mutex-style locks are very robust and predictable while signals turn every future code change into an hard to reproduce race condition (I had to maintain a C++ multi-threading server that used signals for synchronization - signals has so many pitfalls it's just not funny)
Nir
+3  A: 

Building a signalling device out of locks is craziness; just use the signalling device that already exists in the framework.

http://msdn.microsoft.com/en-us/library/system.threading.autoresetevent.aspx

That is: thread one says "hey thread two, start this animation and signal me on this wait handle when you're done." Thread one then waits on the waithandle. Thread two starts the animation, and in its completion event, signals the waithandle. Thread one then wakes up again and says "hey thread two, start this other animation..."

Eric Lippert
Hello Eric, I tried like you adviced, however it's not working for me. Could you please have a look at this thread: http://stackoverflow.com/questions/3420901/synchronous-blocking-animations-in-wpf Thank you very much! Anyone else maybe found a solution for the problem of this thread?
stefan.at.wpf