【问题标题】:Time synchronization in javajava中的时间同步
【发布时间】:2010-11-26 04:32:32
【问题描述】:

在 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,

马库斯

【问题讨论】:

    标签: java synchronization multithreading timestamp simulation


    【解决方案1】:

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

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

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

    import static java.util.concurrent.*
    
    Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(task, 0, 1, TimeUnit.SECONDS)
    

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

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

    (强调我的)

    【讨论】:

    • 不管实时问题如何,他应该为 (1000-processingtime) 睡觉不是更简单的问题吗?
    • 1000 处理时间问题是代码中的一个错误。我的回答是解释为什么整个方法有缺陷,是一个正确的答案,我不太明白它是如何值得反对的
    • 好吧,我认为直接的问题代码中的错误在哪里。然而,跟进你不能可靠地做到这一点的问题是值得的。也许反对票太苛刻了。已删除。
    • @oxbow_lakes 如果任务的执行时间超过一秒,您是否知道示例中的执行程序的行为?我不确定是否必须用同步块包围代码的某些部分,以避免代码同时被多个实例执行...
    • @Markus - 由于执行器是单线程的,它不可能同时运行多个任务。我的(几乎肯定是正确的)猜测是,当以固定速率进行调度时,如果第一次调用花费的时间比该时间长,那么下一次调用将立即开始。
    【解决方案2】:

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

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

    【讨论】:

    • +1 因为我认为这是解决代码中直接错误的唯一答案。
    • Mhhh,我不确定 1000 - processingTime 是否是我真正想要的,因为在这种情况下,睡眠时间仅基于最后一步的信息。使用我的解决方案,我将能够避免一些时间漂移问题。但是因为我对执行开销没有影响,所以我可能别无选择,只能使用您的解决方案......或者?
    • 唯一真正接近您想要的解决方案是其他 cmets 中的 scheduleAtFixedRate - 如果我正确理解您的问题,它的缺点是您无法确定一个步骤之前完成下一个开始(在不同的线程和所有线程上运行) - 如果这不是问题,那么 ScheduledExecutorService 将确保每个步骤在前一个步骤之后 1000 毫秒开始
    【解决方案3】:

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

    看看ScheduledExecutorService

    它看起来像这样:

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

    MyCode类实现runnable接口,每秒执行一次。

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

    【讨论】:

      【解决方案4】:

      正如others 强调的那样,您应该休眠 1000 - 处理时间。

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

      【讨论】:

        【解决方案5】:

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

        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 不准确,但不可能有这么远。我认为要么您在调试器中中断程序,要么代码与输出不匹配。

        【讨论】:

        • 哦,对不起。我忘了说,我目前并没有真正睡觉。因此,这些值可能与您的预期不同。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-26
        • 1970-01-01
        • 1970-01-01
        • 2017-04-11
        相关资源
        最近更新 更多