3

在 for 循环中,我通过检索和处理车辆信息来控制基于模拟步骤的交通模拟器 SUMO。为了确保我的程序“实时”模拟(1 个模拟步骤 = 1 秒),我想在处理阶段之后让我的程序休眠,直到下一个时间步骤开始。为了获得更好的结果,我正在根据最初采用的参考时间戳计算时间戳。

循环如下所示:

    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 ..."); 

这是一些变量的输出:

步数:0                                                                                                         
前时间戳 1252317242565                                                                                   
参考时间:1252317242565                                                                                   
处理时间:394                                                                                            
测试时间:1252317243565                                                                                        
后时间戳 1252317242959                                                                                    
睡眠时间:606                                                                                                  
第1步                                                                                                         
前时间戳 1252317242961                                                                                   
处理时间:665                                                                                            
测试时间:1252317244565                                                                                        
后时间戳 1252317243626                                                                                    
睡眠时间:939(预计:1000 - 665 = 335)                                                                                                  

如您所见,睡眠时间仅对第一个模拟步骤是正确的。我不知道我在这里可能出了什么问题。有人有想法吗?

BR,

马库斯

4

5 回答 5

10

由于 Java 不提供关于线程调度的绝对保证,因此您无法使用标准 Java睡眠确切的时间。Java 会受到操作系统分配 CPU 时间的影响,并且您的程序将受到不可预测的垃圾收集暂停的影响。

如果您确实需要可预测的执行,那么您需要查看Java 的实时规范这显然是矫枉过正!

您可以ScheduledExecutorServicejava.util.concurrent包中使用来定期执行任务(通过休眠一段时间或以特定速率执行)。用法:

import static java.util.concurrent.*

Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(task, 0, 1, TimeUnit.SECONDS)

但这并不准确(参见 JavaDoc):

但请注意,由于网络时间同步协议、时钟漂移或其他因素,相对延迟的到期不必与启用任务的当前日期一致

(强调我的)

于 2009-09-07T10:07:19.553 回答
4

为什么不睡觉1000 - processingTime呢?这将是您最接近正确答案的地方。

您的解决方案仅适用于第一步,因为它仅适用于第一步。您假设您将在 开始处理每个步骤referenceTime + (step * 1000),但您没有考虑开销(线程休眠、打印、垃圾收集)。打印出来 referenceTimeStamp + ((i + 1) * 1000) - beforeTimeStamp 看看我的意思

于 2009-09-07T10:08:21.503 回答
3

从 Java 5 开始,Java 中有一个简单的标准解决方案。

看看ScheduledExecutorService

它看起来像这样:

        ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
        service.scheduleAtFixedRate(new MyCode(), 0, 1, TimeUnit.SECONDS);

其中 MyCode 类实现了可运行接口,并将每秒执行一次。

这不是实时保证,但对于您的情况应该足够了。

于 2009-09-07T10:18:33.203 回答
2

正如其他人所强调的那样您应该睡 1000 个处理时间。

忽略 Java SE 不提供实时平台的事实(因此您不会获得精确度),我可能会看一下Timer类,尤其是Timer.scheduleAtFixedRate(),它将照顾定期安排任务。

于 2009-09-07T10:18:22.220 回答
0

你确定输出来自同一个程序吗?查看内联突出显示的差异,

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                                                                                                

拳头睡眠有 604 (606-2) 不足。所以 939 (335 + 604) 是第二个循环的正确睡眠时间。

Java 的 sleep 不准确,但也不能相差这么远。我认为要么您在调试器中中断程序,要么代码与输出不匹配。

于 2009-09-07T11:10:28.227 回答