问题
Inside a for-loop I'm controlling the simulation-step-based traffic simulator SUMO by retrieving and processing information of vehicles. To make sure that my program simulates in "real-time" (1 simulation-step = 1 second) I want to sleep my program after the processing phase until the next time step begins. To get better results I'm calculating the time stamp based on a initially taken reference time stamp.
The loop looks like this:
System.out.println("start of traffic simulation ...");
for (int i = 0; i < stepCount; i++)
{
System.out.println("step: " + i);
// set before timeStamp
beforeTimeStamp = System.currentTimeMillis();
if (firstStep)
{
// get reference timeStamp
referenceTimeStamp = beforeTimeStamp;
firstStep = false;
}
else
{
// get next vehicleVector
vehicleVector = masterControl.traCIclient.simulateStep();
}
// process vehicleVector
// set after timeStamp
afterTimeStamp = System.currentTimeMillis();
processingTime = afterTimeStamp - beforeTimeStamp;
// calculate sleepTime
sleepTime = referenceTimeStamp + ((i + 1) * 1000) - afterTimeStamp;
// sleep for sleepTime ms
Thread.sleep(sleepTime);
}
System.out.println("end of traffic simulation ...");
Here's the output of some variables:
step: 0 beforeTimeStamp 1252317242565 reference time: 1252317242565 processing time: 394 test time: 1252317243565 afterTimeStamp 1252317242959 sleepTime: 606 step: 1 beforeTimeStamp 1252317242961 processing time: 665 test time: 1252317244565 afterTimeStamp 1252317243626 sleepTime: 939 (exspected: 1000 - 665 = 335)
As you can see the sleep time is only correct for the first simulation step. I have no clue what might me going wrong here. Does anybody has an idea?
BR,
Markus
回答1:
Why not sleep for 1000 - processingTime
? It would be the closest you can get to a correct answer.
Your solution only works on the first step because it's only correct for the first step. You assume that you will start your processing for each step at referenceTime + (step * 1000)
, but you are not taking overhead (thread sleeping, prints, garbage collection) into account.
Print out referenceTimeStamp + ((i + 1) * 1000) - beforeTimeStamp
to see what I mean
回答2:
You cannot sleep for an exact amount of time using standard Java due to the fact that Java offers no absolute guarantees about thread-scheduling. Java is affected by when it is allotted CPU time by the OS and your program will be affected by garbage-collection pauses, which are unpredictable.
If you do need predictable execution then you need to look at the realtime-specification for Java. Which is clealy overkill here!
You can use ScheduledExecutorService
in the java.util.concurrent
package to execute tasks periodically (either by sleeping for a period, or executing at a particular rate). Usage:
import static java.util.concurrent.*
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(task, 0, 1, TimeUnit.SECONDS)
But this is not exact (see the JavaDoc):
Beware however that expiration of a relative delay need not coincide with the current Date at which the task is enabled due to network time synchronization protocols, clock drift, or other factors
(Emphasis mine)
回答3:
There is a simple standard solution in Java starting with Java 5.
Take a look at ScheduledExecutorService.
It will look something like this:
ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
service.scheduleAtFixedRate(new MyCode(), 0, 1, TimeUnit.SECONDS);
Where MyCode class implements runnable interface and will be executed once a second.
This is not a real time guarantee but should be sufficient for your case.
回答4:
As highlighted by others, you should be sleeping for 1000 - processing time.
Ignoring the fact that Java SE doesn't provide a real-time platform (and as such you won't get precision), I would perhaps take a look at the Timer class, and particularly Timer.scheduleAtFixedRate(), which will look after scheduling tasks at regular intervals.
回答5:
Are you sure the output is from the same program? See the discrepancy highlighted inline,
step: 0
beforeTimeStamp 1252317242565
reference time: 1252317242565
processing time: 394
test time: 1252317243565
afterTimeStamp 1252317242959
sleepTime: 606 #### It didn't sleep this long at all
step: 1
beforeTimeStamp 1252317242961 #### This is only 2ms from last afteTimeStamp
processing time: 665
test time: 1252317244565
afterTimeStamp 1252317243626
sleepTime: 939 (exspected: 1000 - 665 = 335) #### This is to make up the deficit from last sleep
There were 604 (606-2) deficit in fist sleep. So 939 (335 + 604) is the correct sleep time for second loop.
Java's sleep is not accurate but it can't be this far off. I think either you interrupt the program in a debugger or the code doesn't match the output.
来源:https://stackoverflow.com/questions/1388538/time-synchronization-in-java