(redirected from MultiThread)


Robo Home | Changes | Preferences | AllPages

In Robocode, using additional threads to deal with a part of the task your bot is performing.

There are several issues to consider when using threads in Robocode:

Bots using multithreading:

If anyone has aditional information about threads, please add it.

-- Albert

I didn't manage to find the original thread but this one where DavidAlves tell us :"Threads. Actually I'm not sure I'd ever want thread support to be added, since Paul Evans pointed out the possibility of writing malicious threads that only execute during your opponent's turn in order to cause your opponent to skip turns." in a discussion about robocode 2. Someone as more info ? -- Synnalagma

Processing time in threads does not count towards the time limit per tick, and threads are not halted during the enemy's turn. So it would be possible (but not ethical) to slow down the processor during the other bots' turn to make them skip turns. -- ABC

If you use threads in your robot, you MUST spawn those threads from within your main Robot class. Calling threadName.start() from anywhere but your main Robot class will result in a rather nasty SecurityManager? exception, something like "java.security.AccessControlException?: Preventing package.class from access to threadgroup: Robot Loader Group". Don't forget to also stop all your threads from within onDeath() and onWin() or else Robocode will not give you a score, so the IBM forums tell me. There is also apparently a limit of 5 threads per robot. -- Nathanael

Is there a simple code sniplet to demonstrate multitreading? thx --SSO

Here's a simple thread I made to help me figure out some sample botcode I got from this wiki. It's implemented as an inner class inside the main bot class - this is convenient because inner classes can use the properties and methods of the enclosing class.

 private class Logger extends Thread {

     *     Thread which logs the number of times the bot changes direction
     *     Each change is accumulated into mainclass property "directionChangesCount?" by the move code
    public void run()       {

    	do  {
            logToConsole?( "Ticks:  " + getTime() + "   Dir changes:  " + getDirectionChangesCount?() ) ;
            try     {
                Thread.sleep( 1000 ) ;  // milliseconds
            catch (InterruptedException? e)  {}  // no need to react to an interrupt
         }  while ( isLogThreadSemaphore?() ) ;

        logToConsole?( "Leaving Logger.run()" ) ;
        logToConsole?( "Ticks:  " + getTime() + "   Dir changes:  " + getDirectionChangesCount?() ) ;



Here's the code to start the thread (called during initialization, from the run() method of the bot):

    //  Start status logging. The thread and the semaphore are properties of the main class, with 
    //  accessor methods.
    setLogThread( new Logger() ) ;
    setLogThreadSemaphore?( true ) ;
    getLogThread().start() ;

And the stop code (also needed in onWin()):

    public void onDeath( DeathEvent? e )    {
	setLogThreadSemaphore?( false ) ;

Notes about threads (in case anyone cares):

  1. You can implement interface Runnable instead of subclassing Thread. Usually better, because then you can inherit from another class (not needed here).
  2. The semaphore is a simple way to asynchronously signal a "stop command" to the thread. After the thread wakes up, it shuts itself down by falling out of its run() method.
  3. For a first-time user of threads, calling xxx.start() to execute the run() method looks a bit strange, but that's the correct way to do it. Java has to do some setup before executing a new thread - that's done via start(), which then calls Thread.run()
  4. There is rarely any requirement in simple programs to use ThreadGroups? or to interrupt your own threads. The Robocode engine itself probably has to do that, but I can't see why a bot would.
  5. Java 1.5 has some nice new classes to handle threads (at last - the original implementation has some annoying limitations)
  6. Using properties in the main class for the thread and the semaphore is (obviously) only ok in the special case where only one instance of the thread will be created. With multiple instances, I usually make an XxxThreadManager? class which holds the variables and tracks the threads. I think this is one of the things which is implemented in a smarter way in java 1.5 - I'll update this if/when I test the new features.
  7. FWIW it's possible for something like the Robocode engine to find all the threads started from a program and stop them all from running for a while. I'd certainly like to see threading supported in [RobocodeNG], because it's the natural pattern for managing things like the bot radar. OTOH it shouldn't be compulsory, because correct multithreaded coding adds a "complexity overhead" which would add even more stress to the "simple things should be easy to do" principle which is already (IMO) an issue with Robocode. -- ycn228

Nice writeup ycn228 but a few comments anyway :)

  1. Why use threads in robocode? Don't get me wrong, I generally love threads and multithread as often as I can motiviate it. With robocode I can't motivate it. At least not with the algorithms I'm using today that is :) Robocode is turn/tick-based and I need to synchronize it with the ticks to make sure I don't mix data from previous turns when I shouldn't for example. The above example with direction changes is a lot easier to solve by just checking the time difference between now and the last time it was printed.
  2. Having said that the semaphore you use above needs to be either declared volatile or accessed in a synchronized block (for example make sure isLogThreadSemaphore? and setLogThreadSemaphore? methods are declared synchronized). Otherwise the "normal" thread and that thread might not have the same view of the variable. And yes the java 5 thread api is wonderful!
  3. I agree with the "simple things should be easy to do" and to that effect I actually the other day I started a small framework that should make it easy to code robots in robocode. It separates out radar, gun movements etc naturally and provides basic implementations of them. Focus on simplicity and extensibility (not always compatible things for sure!). It's nothing major but a nice thing. Next version of PulsarMax will be based on it. If I find the time :) -- Pulsar

To answer "Why use threads in Robocode?" I would say that some algorithm can take too much time if processed in the main thread. The segmentation tree in Toad takes about 175 ticks at the end of a 35 rounds game. So without threads I could not do it. I only use one mutex to avoid inserting leaves, when rebuilding. -- Florent

Sounds like a good case for threads. Not too easy to rebuild such a structure asynchronously though, definatley only for the more advanced and thread-experienced. -- Pulsar

You might be right about threading in Robocode Pulsar. I prefer the code structure you get with threading, but I've learned that I can't "stack" a sequence of calls to the same setXX methods (like several calls for turning the radar), and I think that means handling the radar, movement, and turret in separate threads would be more trouble with threading rather than less. Florent's situation is different of course. BTW - just to be picky :) my semaphore is a static field in the enclosing class, so it works like a volatile variable in the thread class. When I first learned thread coding in Java many JVMs didn't actually implement "volatile", so the normal practice was to code your own. That was Java 1.1.8 to 1.3.1 though - I guess the standard of JVMs has improved since then? Anyway if no-one else does it, I'll add a writeup on how to handle shared data and signalling between threads later on, but I want to check out the new 1.5 features first, because it's all such a PITA with the pre-1.5.implementation. -- ycn228

Now that you need to clarify for me. Yes you are right about the volatile field not being implemented correctly in old JVMs. I've been coding in java since -96 and the then new java 1.0. Furthermore it isn't until the new memory model with JSR 133 (partly fixed in java 1.4 and more so in 1.5) that the specification was updated to remove certain problems with synchronization, volatile being one of them as well, which I hope you are aware of. For example volatile variables, such as a declared volatile boolean, could not be used to mark something as initilized or similar where you needed other stuff to have been set (the order of volatile reads and writes were ok, but not other instructions around them). Florent could not use it for example to mark the tree as updated. But on to the question at hand:

I have a problem with this statement: "my semaphore is a static field in the enclosing class, so it works like a volatile variable in the thread class".

First of all, looking at the example code the use of semaphore here in thread terms is a bit misleading, but a semaphore can be a flag I guess in general terms. But in threading it is better to avoid misunderstandings and only use the word for what is in the common usage there, meaning a type of aquire and release lock, which it is not in your example above.

The second thing is that by delcaring the field static (and maybe along with as it is an "enclosing class") would somehow magically make the variable volatile? Maybe I'm missing something (very possible), but I don't think so. The JLS is very clear about references (read or writes) to variables from different threads, including static variables. They need to be volatile or otherwise thread synchronized to get a consistent view at any one time. Class variables (static) are part of the threads own memory view. By the way classes can be "synchronized on" too to synchronize the view of static variables.

The workaround for volatile fields was to use the monitors (synchronized) instead. -- Pulsar

Interesting comments Pulsar - to be honest I've never tried to study Java threads at the level you are describing them, for two reasons:

  1. Because of all the problems you are explaining.
  2. Because the explanations are usually incomprehensible. Yours is the one of the more lucid explanations I've seen so far.
Anyway, re the "semaphore":

I've always assumed that this means the thread tests the boolean in the main class directly, which is why I said it has the same effect as declaring the field volatile. I've never tried to check that it really happens this way. There's still nothing stopping other threads from updating at the same time as I'm reading, but since it's just a boolean controlling a short timer loop, and there is no code anywhere which can change the signal from "stop" back to "start" it doesn't actually matter. The worst that can happen is that I loop once more before shutting down. This just means that it works ok in this situation though - I agree that it's not actually protected from possible weird side effects of unsynchronized updates.

Bottom line for me is that I'll take a good look at the new Java 1.5 features RSN. IIRC there are fields which can't be accessed by more than one thread at a time, and also better ways to start/stop threads. You can expect a lot of questions from me immediately after I read up on 1.5 :-)
-- ycn228

Ok just one more thing then: "The worst that can happen is that I loop once more before shutting down." Actually it is never explicitly guaranteed to stop. In practice that will never happen but it sure can cause more than one extra loop. Threads all have their own view of the memory in java for performance reasons, a very good thing. But this means that the robocode threads and your thread both have their own copy of the flag. To make sure that the threads' view of what the variable actually contain are the same you neeed to synchronize those threads (see were that keyword in java is coming from now?). Synchronizing on the same object (monitor) makes sure that the view of those threads synchronizing have the same view of the state of variables (at that time). Variables declared volatile are required to be read and written directly to the "master"/main memory so that all threads are forced to have the same view always: This also incurs a, usually small, performance hit.-- Pulsar

Toad is certainly on the slow side, but on my MacBook, it still runs at quite a reasonable speed - a 35 round battle takes about a minute or less. However, on my Linux machine (running Ubuntu), which is generally 1/2 to 2/3 the speed of my MacBook for Robocode, it runs at about 10 FPS. It's running Sun Java 1.5. Anyone have some insight about this? (It basically means Robocode hangs forever if the RR@Home client on that machine catches a Toad battle, and I have to just restart it once I catch it.) -- Voidious

Just reading part of this page, shows me I should leave the threads to those who won't cause robocode and java itself to shutter to a halt by using them. Thought it was a nice idea on how to impliment the massively slow k-type clustering and some neural-netowrks, which don't necessarily need to be syncronized. However the complications of that, are more then I can probably figure out. --Chase-san

Robo Home | Changes | Preferences | AllPages
Edit text of this page | View other revisions
Last edited January 29, 2007 8:51 EST by Chase-san (diff)