Difference between revisions of "Multithreading"

From Robowiki
Jump to navigation Jump to search
(New page created when I felt like editing the information on the old wiki page.)
 
m (quick cleanup)
Line 1: Line 1:
 
Threads can be used to offload big chunks of work without skipping turns. Robocode imposes some specific constraints on robots using threads:
 
Threads can be used to offload big chunks of work without skipping turns. Robocode imposes some specific constraints on robots using threads:
 +
 
# You can only run 5 threads (not including your main thread) at a time. Previous threads have to terminate before the security manager will let you run more.
 
# You can only run 5 threads (not including your main thread) at a time. Previous threads have to terminate before the security manager will let you run more.
 
#* This does not affect thread ''creation''. You can create as many Thread objects as you wish, but you may only run 5 at a time.
 
#* This does not affect thread ''creation''. You can create as many Thread objects as you wish, but you may only run 5 at a time.
Line 9: Line 10:
 
Needless to say, threading is not for the faint of heart.
 
Needless to say, threading is not for the faint of heart.
  
==Fairness Concerns==
+
== Fairness Concerns ==
 
Since threads aren't stopped during the enemy's turn, they can be used to lower the enemy's score. You could spawn a thread which does heavy work during the enemy's turn to increase the chances of them skipping a turn. Needless to say, this is not cool.
 
Since threads aren't stopped during the enemy's turn, they can be used to lower the enemy's score. You could spawn a thread which does heavy work during the enemy's turn to increase the chances of them skipping a turn. Needless to say, this is not cool.
  
 
Because of these concerns, robot multithreading may not always be supported by Robocode into the future.
 
Because of these concerns, robot multithreading may not always be supported by Robocode into the future.
  
==Sample Code==
+
== Sample Code ==
 
The following code spawns 5 threads (one per turn) which print a message each turn for 100 turns, then terminate.
 
The following code spawns 5 threads (one per turn) which print a message each turn for 100 turns, then terminate.
  
 
<pre>
 
<pre>
 
import robocode.*;
 
import robocode.*;
 +
 
public class ThreadDemo extends AdvancedRobot {
 
public class ThreadDemo extends AdvancedRobot {
 +
 
     // This will be used to tell our threads to terminate. To do this, we must
 
     // This will be used to tell our threads to terminate. To do this, we must
 
     // be able to modify its value, so a simple Boolean object will not do.
 
     // be able to modify its value, so a simple Boolean object will not do.
Line 25: Line 28:
 
     // this, it must inherit from Object to get at its notifyAll() method.
 
     // this, it must inherit from Object to get at its notifyAll() method.
 
     final boolean[] token = {true};
 
     final boolean[] token = {true};
 +
 
     // Number of threads we have spawned.
 
     // Number of threads we have spawned.
 
     int threadCount = 0;
 
     int threadCount = 0;
Line 30: Line 34:
 
     @Override
 
     @Override
 
     public void run() {
 
     public void run() {
 +
 
         // Get the radar spinning so we can get ScannedRobotEvents
 
         // Get the radar spinning so we can get ScannedRobotEvents
 
         setTurnRadarRightRadians(Double.POSITIVE_INFINITY);
 
         setTurnRadarRightRadians(Double.POSITIVE_INFINITY);
 +
 
         while (true) {
 
         while (true) {
 
             out.println("Robot time: " + getTime());
 
             out.println("Robot time: " + getTime());
 +
 
             if (threadCount < 5) {
 
             if (threadCount < 5) {
 
                 final long spawnTime = getTime();
 
                 final long spawnTime = getTime();
 +
 
                 // Quick and dirty code to create a new thread. If you don't
 
                 // Quick and dirty code to create a new thread. If you don't
 
                 // already know how to do this, you probably haven't learned all
 
                 // already know how to do this, you probably haven't learned all
 
                 // the intricacies involved in multithreading on the JVM yet.
 
                 // the intricacies involved in multithreading on the JVM yet.
 +
 
                 new Thread(new Runnable() {
 
                 new Thread(new Runnable() {
 +
 
                     int runCount = 0;
 
                     int runCount = 0;
 +
 
                     public void run() {
 
                     public void run() {
 +
 
                         synchronized(token) {
 
                         synchronized(token) {
 
                             while (token[0] == true && runCount < 100) {
 
                             while (token[0] == true && runCount < 100) {
Line 47: Line 59:
 
                                     "\tHi from Thread#%d (current time: %d). Repeat count: %d\n",
 
                                     "\tHi from Thread#%d (current time: %d). Repeat count: %d\n",
 
                                     spawnTime, getTime(), ++runCount);
 
                                     spawnTime, getTime(), ++runCount);
                                 try{
+
                                 try {
 
                                     // Sleep until re-awakened in next turn
 
                                     // Sleep until re-awakened in next turn
 
                                     token.wait();
 
                                     token.wait();
Line 54: Line 66:
 
                         }
 
                         }
 
                     }
 
                     }
 +
 
                 }).start();
 
                 }).start();
 +
 
                 threadCount++;
 
                 threadCount++;
 +
 
             }
 
             }
 +
 
             execute();
 
             execute();
 
         }
 
         }
Line 74: Line 90:
 
     @Override
 
     @Override
 
     public void onWin(WinEvent e) {
 
     public void onWin(WinEvent e) {
 +
 
         // Politely tell our threads to stop because the round is over
 
         // Politely tell our threads to stop because the round is over
         synchronized(token) { token[0] = false; }
+
         synchronized(token) {
 +
            token[0] = false;
 +
        }
 
     }
 
     }
  
 
     @Override
 
     @Override
 
     public void onDeath(DeathEvent e) {
 
     public void onDeath(DeathEvent e) {
 +
 
         // Politely tell our threads to stop because the round is over
 
         // Politely tell our threads to stop because the round is over
         synchronized(token) { token[0] = false; }
+
         synchronized(token) {
 +
            token[0] = false;
 +
        }
 
     }
 
     }
  
Line 87: Line 109:
 
     @Override
 
     @Override
 
     public void onBattleEnded(BattleEndedEvent e) {
 
     public void onBattleEnded(BattleEndedEvent e) {
 +
 
         // Politely tell our threads to stop because the battle has been ended.
 
         // Politely tell our threads to stop because the battle has been ended.
 
         // This gets called whether the battle was aborted or ended naturally,
 
         // This gets called whether the battle was aborted or ended naturally,
 
         // so beware of duplication with onDeath/onWin (if that is important to you).
 
         // so beware of duplication with onDeath/onWin (if that is important to you).
         synchronized(token) { token[0] = false; }
+
         synchronized(token) {
 +
            token[0] = false;
 +
        }
 
     }
 
     }
 
}
 
}
 
</pre>
 
</pre>
==Related Links==
+
 
* [http://old.robowiki.net/robowiki?MultiThreading Multithreading discussion on the old wiki]
+
== Related Links ==
 +
* [[oldwiki:MultiThreading|Multithreading discussion on the old wiki]]

Revision as of 14:41, 27 January 2010

Threads can be used to offload big chunks of work without skipping turns. Robocode imposes some specific constraints on robots using threads:

  1. You can only run 5 threads (not including your main thread) at a time. Previous threads have to terminate before the security manager will let you run more.
    • This does not affect thread creation. You can create as many Thread objects as you wish, but you may only run 5 at a time.
  2. You must stop your threads at the end of each round, or you will throw an Exception (and all the nasties that entails).
    • This means you must override both onWin(...) and onDeath(...) to ensure your child threads get stopped at the end of every round, win or lose.
  3. You must also stop your threads at the end of the battle, or your robot will throw an Exception if the user stops a battle before it finishes. It's also annoying having to wait for robots to be killed when they don't do this (especially if you're debugging them!)
    • To ensure your robot also gets stopped when the user aborts a battle, you must override onBattleEnded(...) and stop your threads if they haven't stopped already.

Needless to say, threading is not for the faint of heart.

Fairness Concerns

Since threads aren't stopped during the enemy's turn, they can be used to lower the enemy's score. You could spawn a thread which does heavy work during the enemy's turn to increase the chances of them skipping a turn. Needless to say, this is not cool.

Because of these concerns, robot multithreading may not always be supported by Robocode into the future.

Sample Code

The following code spawns 5 threads (one per turn) which print a message each turn for 100 turns, then terminate.

import robocode.*;

public class ThreadDemo extends AdvancedRobot {

    // This will be used to tell our threads to terminate. To do this, we must
    // be able to modify its value, so a simple Boolean object will not do.
    // It will also be used to make our threads wait until the next round. For
    // this, it must inherit from Object to get at its notifyAll() method.
    final boolean[] token = {true};

    // Number of threads we have spawned.
    int threadCount = 0;

    @Override
    public void run() {

        // Get the radar spinning so we can get ScannedRobotEvents
        setTurnRadarRightRadians(Double.POSITIVE_INFINITY);

        while (true) {
            out.println("Robot time: " + getTime());

            if (threadCount < 5) {
                final long spawnTime = getTime();

                // Quick and dirty code to create a new thread. If you don't
                // already know how to do this, you probably haven't learned all
                // the intricacies involved in multithreading on the JVM yet.

                new Thread(new Runnable() {

                    int runCount = 0;

                    public void run() {

                        synchronized(token) {
                            while (token[0] == true && runCount < 100) {
                                System.out.printf(
                                    "\tHi from Thread#%d (current time: %d). Repeat count: %d\n",
                                    spawnTime, getTime(), ++runCount);
                                try {
                                    // Sleep until re-awakened in next turn
                                    token.wait();
                                } catch (InterruptedException e) {}
                            }
                        }
                    }

                }).start();

                threadCount++;

            }

            execute();
        }
    }

    @Override
    public void onScannedRobot(ScannedRobotEvent event) {
        synchronized(token) {
            // Wake up threads! It's a new turn!
            token.notifyAll();
        }
    }

    // The following events MUST be overriden if you plan to multithread your robot.
    // Failure to do so can cause exceptions and general annoyance.

    @Override
    public void onWin(WinEvent e) {

        // Politely tell our threads to stop because the round is over
        synchronized(token) {
            token[0] = false;
        }
    }

    @Override
    public void onDeath(DeathEvent e) {

        // Politely tell our threads to stop because the round is over
        synchronized(token) {
            token[0] = false;
        }
    }


    @Override
    public void onBattleEnded(BattleEndedEvent e) {

        // Politely tell our threads to stop because the battle has been ended.
        // This gets called whether the battle was aborted or ended naturally,
        // so beware of duplication with onDeath/onWin (if that is important to you).
        synchronized(token) {
            token[0] = false;
        }
    }
}

Related Links