Difference between revisions of "Multithreading"
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== | + | |
− | * [ | + | == 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:
- 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.
- 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(...)
andonDeath(...)
to ensure your child threads get stopped at the end of every round, win or lose.
- This means you must override both
- 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.
- To ensure your robot also gets stopped when the user aborts a battle, you must override
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; } } }