This site requires JavaScript, please enable it in your browser!
Greenfoot back
MrBradley
MrBradley wrote ...

2017/5/23

ThreadAct

MrBradley MrBradley

2017/5/23

#
Davin, I've been reviewing the threadact scenario. I think I have a use for it. I have actors that can only perform primitive or basic actions. But others can create aggregate actions based on these primitives. Sometimes in the act method a developer can create a long running method, delaying the execution of other actors. What I am thinking of is using the thread act threading mechanics to relinquish control of the actor thread after the execution of each primitive (basic) action. I am hoping this will have the effect of segmenting the execution of long running aggregate tasks. Speed is not an issue. Thoughts?
davmac davmac

2017/5/24

#
It's a long time since I re-visited that code, but I think it should be suitable for what you're suggesting. Let me know if you have any problems.
MrBradley MrBradley

2017/5/25

#
Davin, In your example you have the actor extend Thread. Is this necessary? Does greenfoot not execute each actor in a separate thread? Is there any place I can go to get more information on how GF manages the execution of its environment & actors? I also noted that each ThreadActor locks on its own sequenced lock (not a shared one). I appears that this is done simple to interrupt the execution of each sequence action. Is this correct.
davmac davmac

2017/5/25

#
MrBradley wrote...
In your example you have the actor extend Thread.
Actually the actor extends ThreadActor, which just interfaces the actor API (act() method) to one or more Sequence objects. It is the Sequence class which extends Thread.
Is this necessary? Does greenfoot not execute each actor in a separate thread?
No, the normal act sequence is to call the act() method of each actor in the world in turn (from a single thread). The Greenfoot API is not designed to be used from multiple threads concurrently (in essence, the Threadact scenario provides a way to use multiple threads to access the API sequentially - it prevents two threads from executing their sequence at the same time).
Is there any place I can go to get more information on how GF manages the execution of its environment & actors?
There is no formal documentation on that; there is a brief discussion in one or two of the tutorials/videos (see the documentation section of the website) but I'm not sure that there's anything which explicitly states that actors act sequentially, for instance. That said, it is not very complicated. Each act cycle, the act method for each actor is called - one after the other - in no particular order. Clicking "Act" in the user interface triggers a single act cycle. Clicking "Run" causes act cycles to be executed continuously, with a delay between cycles (the duration of which depends on the speed setting).
I also noted that each ThreadActor locks on its own sequenced lock (not a shared one). I appears that this is done simple to interrupt the execution of each sequence action. Is this correct.
It is done to facilitate the ability to efficiently run an iteration of an individual sequence. Since each sequence waits on its own lock, you can resume a sequence by calling notify() on the lock for that sequence; this is what happens in the performSequence() method. If sequences shared a single lock you would need to wake them all (via notifyAll()) just so that one of them could perform its next iteration. You are correct that the lock is used for waiting between sequence iterations (in the method). I hope that it is clear now, feel free to ask for clarification.
MrBradley MrBradley

2017/5/25

#
This does clarify some things for me. Example: I have a scenario where actors have primitive move and turn methods. Students are allowed to build additional capabilities by building aggregate methods. For example: class ActorBase extends Actor { void move() {}; void turn90(){}; } class Sentry extends ActorBase { void act() { if( isAtEdge()) { turn90(); turn90(); } else move(); } } // traverses back and forth This version will interleave its actions with other Sentrys, but if a student creates a traverseWorld() method that is called from act(), then their Sentry will execute that entire sequence before other Actors' act() methods are called. Most actors utilized by students will be built upon a known set of primitives. My interest is to add infrastructure to the ActorBase that waits after each primitive action, thus allowing other actors to execute their primitive actions. Since each Actor is called from a single thread, any attempt to wait will just block GF. It appears therefore that ActorBase should contain an executable object (PrimitiveAction) that is started in its own thread, as in the ThreadActor scenario. The intent is to allow students to create long running aggregate methods but enforce the execution of their primitives to run in parallel with other actors. I was thinking of having the World act() method call notifyAll() to let all waiting actions (threads) continue. And have all threaded primitives share a world lock, and wait after each primitive is executed. Another approach is to pump messages into a message queue (like sequence). Thoughts?
MrBradley MrBradley

2017/5/25

#
Here are some ideas: 1st push the sequence processing into the infrastructure as follows:
public abstract class ActionSequence extends Sequence
{
    public void doRun() throws InterruptedException
    {            
        while (true) {
            act();
            waitForNextSequence();
        }
    } 

    public abstract void act();
}
Using the GF nomenclature you can now rewrite the MoveSequence as follows:
    class MoveSequence extends ActionSequence
    {
        public void act()
        {
            setLocation(getX() + 1, getY());            
        }
    }
If you are going to have a dynamic lists of sequences you will need not only to determine the correct termination condition but potentially have to do nothing for a few cycles. Therefore:
public class IdleSequence extends ActionSequence 
{
    // do nothing
    public void act() {};
}
With this new structure I can implement the primitive actions I need. Thoughts?
davmac davmac

2017/5/26

#
I was thinking of having the World act() method call notifyAll() to let all waiting actions (threads) continue. And have all threaded primitives share a world lock, and wait after each primitive is executed. Another approach is to pump messages into a message queue (like sequence).
I would go with the second option, since the API is not threadsafe. If multiple actors were to call move() or similar methods from multiple threads at the same time, internal data structures could get corrupted. Regarding the code you posted in the previous post, I would note that you're not getting much beyond plain actors, other than the ability to call waitForNextSequence() in order to pause between actions. Of course that is arguably the important thing.
MrBradley MrBradley

2017/5/26

#
Davin, Thanks for the feedback. I will use a Producer/Consumer model for the message pump approach. I always try to design and build solutions that can be used as a learning opportunity for my students. In the example above I have stripped away all of the unnecessary elements to focus on the pause between actions. I still need to ensure that the pause will work across multiple actors to solve the long running (aggregate of primitives actions) method calls. On another note: Any plans to change Actors from a class to an interface? ;)
You need to login to post a reply.