7

我有一个线程,我需要每 10 毫秒执行一次。所以我有非常简单的代码,就像这样:

while (work) {
    // do something
    Sleep(10000); // boost sleep can be also used
}

我听说Sleep一般不推荐这样做,如果我用deadline_timer整体应用程序性能代替它会更好,特别是我会避免昂贵的“上下文切换”。

我应该改成sleepdeadline_timer?如果可以,有人可以举个例子吗?

4

2 回答 2

19

这一切都取决于对 10ms 的要求。


迭代之间有 10 毫秒的延迟

如果应用程序在迭代之间需要 10 毫秒的延迟,那么 sleep 就可以了。假设work()完成需要 7 毫秒,时间线将导致以下结果:

时间 | 行动
--------+------------
0.000 秒 | 开始工作
0.007s | 完成工作,阻止
0.017s | 完成阻塞,开始工作
0.024s | 完成工作,阻止
0.034s | 完成阻塞,开始工作

可能值得考虑使用 Boost.Thread 的this_thread::sleep_for()可读性:

#include <boost/thread.hpp>

int main()
{
  for (;;)
  {
    work();
    boost::this_thread::sleep_for(boost::chrono::milliseconds(10));
  }
}

迭代之间的最大延迟为 10 毫秒

如果迭代之间的最大延迟为 10ms,那么执行工作所花费的时间需要从 10ms 延迟减少。假设work()完成需要 7 毫秒,时间线将导致以下结果:

时间 | 行动
--------+------------
0.000 秒 | 开始工作
0.007s | 完成工作,阻止
0.010s | 完成阻塞,开始工作
0.017s | 完成工作,阻止
0.020s | 完成阻塞,开始工作

同步使用计时器教程是一个很好的起点。需要考虑的一点是 Boost.Asio 提供了一些计时器。如果 10ms 延迟不应该受到系统时钟变化的影响,那么可以考虑使用steady_timer. 否则,deadline_timer应该没问题。

#include <boost/asio/steady_timer.hpp>

boost::asio::io_service io_service;
boost::asio::steady_timer timer(io_service);

int main()
{
  for (;;)
  {
    timer.expires_from_now(boost::chrono::milliseconds(10));
    work();
    timer.wait();
  }
}

另一个考虑是如果work()需要 13 毫秒来完成,那么工作之间不会有延迟,因为已经超过了最大延迟。但是,这会导致work()每 13 毫秒执行一次,而不是work()每 10 毫秒执行一次。

时间 | 行动
--------+------------
0.000 秒 | 开始工作
0.013s | 完成工作,阻止
0.013s | 完成阻塞,开始工作
0.026s | 完成工作,阻止
0.039s | 完成阻塞,开始工作

每 10 毫秒执行一次工作

如果完成时间work()超过延迟,则work()不会每 10ms 完成一次。为此,可能需要使用多个线程。以下是具有 2 个线程异步执行工作的时间线,每 10 毫秒调度一次,但需要 13 毫秒才能完成:

时间 | 线程 A | 线程 B
-------+----------------+------------- --------------
0.000 秒 | 安排工作,开始工作|
0.010s | | 安排工作,开始工作
0.013s | 完成工作,块| 高分辨率照片| CLIPARTO
0.020s | 安排工作,开始工作|
0.023s | | 完成工作,阻止
0.030s | | 安排工作,开始工作
0.033s | 完成工作,块| 高分辨率照片| CLIPARTO

异步使用定时器可以提供一个基本的介绍。总体思路是将工作添加到 中io_service,并且每 10 毫秒io_service就会选择一个正在运行的线程来调用work()。线程池大小可以根据work()完成所需的时间量增加或减少。在工作需要 7 毫秒的情况下,单个线程可以异步等待计时器。

#include <boost/asio/steady_timer.hpp>

boost::asio::io_service io_service;
boost::asio::steady_timer timer(io_service);

void handle_timer(const boost::system::error_code& error);

void schedule_work()
{
  // Schedule more work.
  timer.expires_from_now(boost::chrono::milliseconds(10));
  timer.async_wait(&handle_timer);
}

void handle_timer(const boost::system::error_code& error)
{
  if (error) return;
  schedule_work();
  work();
}

int main()
{
  // Add work to io_service.
  schedule_work();

  // Create 2 threads that will run io_service.
  boost::thread_group threads;
  for (std::size_t i = 0; i < 2; ++i)
    threads.create_thread(boost::bind(
      &boost::asio::io_service::run, &io_service));

  // Wait for threads to finish.
  threads.join_all();
}

当引入并发以满足最后期限时,验证它work()是线程安全的。

于 2013-05-03T16:52:16.943 回答
1

使用Sleep()没问题。我假设它是 Windows API 函数,无论如何都需要毫秒,所以如果你想要 10 毫秒,你可能应该传递 10 而不是 10000。

像这样使用 sleep 的简单程序的最大问题可能是漂移。如果间隔需要非常精确,您将面临一些挑战。你没有说你是否关心如果你的实际逻辑需要几毫秒才能完成会发生什么——你可以在 10 毫秒后开始下一次迭代,或者更短的时间来“赶上”。

于 2013-05-03T10:19:52.387 回答