【问题标题】:unexpected delay in main thread主线程意外延迟
【发布时间】:2022-02-03 21:39:01
【问题描述】:

这是一个使用线程测试循环调用的小 C++ 代码。但有时会因为意外延迟而失败。应该每 10 毫秒调用一次 for 循环。 for-loop 的运行时间通常只有 1ms。但有时执行时间会超过 200ms。看起来其他进程中断了这个 for 循环并在 200 毫秒后返回。这太不可思议了,200ms,这么长的时间。该程序在 GNU Linux 5.10.41 ARM aarch64 下运行。 我该怎么做,使主线程不能被其他进程或线程抢占?非常感谢!

while(1)
{
    auto start_time = std::chrono::high_resolution_clock::now();
    LOG_F(INFO, "cyclic start time");
    for(int j = 0; j < 150000; j++); //
    auto end_time  = std::chrono::high_resolution_clock::now();
    auto exec_time = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
    LOG_F(INFO, "execution time: %dms", exec_time.count());
    if (exec_time.count() < 10)
    {
        LOG_F(INFO, "i = %d", i);
        std::this_thread::sleep_for(std::chrono::milliseconds(10 - exec_time.count()));
    }
    else
    {
        LOG_F(ERROR, "execution time was higher than 10ms (%dms)", exec_time.count());
        break;
    }
}

【问题讨论】:

  • 您可以尝试使用更高优先级的线程或实时线程。这些不太可能被窥视。但是通常如果您的系统不是处于重负载下,您的程序不应被抢占 200 毫秒
  • for(int j = 0; j &lt; 150000; j++); 的目的是什么?它将被优化掉。您只是在衡量日志记录功能的时间成本。
  • 在黑暗中拍摄。记录互斥量。您的日志记录系统中可能存在互斥锁/锁。另一个线程可能是资源占用者,它获取了锁并且没有快速释放它。这将导致 THIS 线程等待比预期更长的时间。通过禁用所有其他线程来检查此行为并查看行为是否继续。
  • 快速测试,将所有日志记录语句移到捕获start_time 和end_time 的位置之外。如前所述,如果您希望for 循环执行任何操作,请确保您正在编译没有优化。但即使数到 150000 也应该非常快,可能在微秒数量级。
  • 非常感谢您的建议。执行了 for 循环,没有优化。在日志文件中更改运行时的循环号也不同。该程序在具有 4 个处理器的 S32G EVB 上运行。使用 top 命令我可以看到 CPU 负载 = 4.3%。即使使用了代码中的线程函数 sleep(),但实际上只有一个主线程。

标签: c++ linux multithreading


【解决方案1】:

它在删除日志语句后工作。日志记录真的是一个像@Freebreeze 提到的互斥锁。实际上日志语句可以保存在代码中,只需使用 -v ERROR 调用程序即可。非常感谢您的帮助!

看起来日志记录是原因。但是为什么记录语句需要这么长时间,但并不总是,就像心跳一样?

【讨论】:

    【解决方案2】:

    用 cmets 的原始答案写的正确答案:

    在黑暗中拍摄。记录互斥量。可能存在互斥锁/锁 在您的日志记录系统中。另一个线程可能是资源占用 获取锁而不是快速释放它。这会导致这个 线程等待的时间比预期的要长得多。通过以下方式检查此行为 禁用所有其他线程并查看行为是否继续。

    在下方跟进帮助:

    @Jung 很高兴你知道了!

    如果日志记录花费的时间不定,请查看您传递给日志记录系统的数据。一般需要注意的是按值传递的大型数据结构。是的,我知道编译器现在很聪明,但只是需要注意一些事情。

    我有时用来减少日志占用的方法之一是使用专用线程将日志语句写入文件。该线程使用 std::vector 或队列。当 vector.size() > 8 时,将其转储到文件中。如果您有设置优先级的方法,那么您也可以分配prio。这让我们进入了 RTOS 的想法。如果您需要严格的时间安排,可以研究一下。

    工作流程

    • 假设 sensor1 线程写入日志语句。
      • Sensor1 线程将数据添加到 std::vec
    • 假设sensor2线程写日志语句
      • Sensor2 线程将数据添加到 std::vec

    ....

    这将有助于最大限度地减少锁定互斥锁所花费的时间以及最大限度地减少打开文件所花费的时间。但是,这是以内存为代价的。调整队列大小以达到您想要的目标。

    【讨论】:

    • 嗨@Freebreeze,谢谢。如果我得到一些有趣的结果,我会试试这个并将结果放在这里。
    • 很好,如果日志语句有自己的线程并根据您的想法使用队列,它也可以工作。
    • 很高兴事情似乎对你有用!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-10
    • 1970-01-01
    • 1970-01-01
    • 2017-01-21
    • 2011-10-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多