带取消功能的准确睡眠

Accurate Sleep with cancellation

我需要实现一个准确一致的延迟或休眠功能,并且必须能够被取消

这是我的代码:

bool cancel_flag(false);

void My_Sleep(unsigned int duration)
{
  static const size_t SLEEP_INTERVAL = 10U; // 10 milliseconds
  while ((!cancel_flag) && (duration > SLEEP_INTERVAL))
  {
    Sleep(duration);
    duration -= SLEEP_INTERVAL;
  }
  if ((!cancel_flag) && (duration > 0U))
  {
    Sleep(duration);
  }
}

上面的函数是运行在一个工作线程中。主线程能够更改 "cancel_flag" 的值以中止(取消)休眠。

在我的店里,当持续时间为 10 秒(10000 毫秒)时,我们有不同的结果。一些 PC 显示 10 秒的睡眠持续时间,其他 PC 显示 16 秒。

有关 Sleep() 函数的文章说它绑定到 windows 中断,并且当持续时间结束时,将重新安排线程(可能不会立即 运行 )。由于重新安排和中断延迟,上述功能可能会遇到时间错误的传播。

Windows Timestamp Project 描述了另一种等待计时器对象的技术。我的理解是这种技术不提供取消方法(通过另一个主线程)。

问题:
1. 如何改进线程延迟或睡眠的实现,可以被另一个任务取消,并且更一致

  1. 是否可以终止休眠的 AFX 线程?
    (当主线程终止休眠的 AFX 线程时会发生什么?)

  2. 当主线程终止调用 WaitForSingleObject 的线程时会发生什么?

准确度应该在 10 毫秒左右,如 10 秒 + 10 毫秒。
结果在各种 PC 上应该是一致的(所有 运行ning Windows 7 或 10)。

背景
具有正确时序的 PC 是 运行ning Windows 7,频率为 2.9 GHz。
时序不正确的 PC 为 运行ning Windows 7,频率为 3.1 GHz,同时执行的任务和应用程序较少 运行ning。
应用程序是使用 Visual Studio 2017 和 MFC 框架(使用 AFX 创建线程)开发的。

你根本不应该实施这个。

在 C++11 中,多线程的所有基本必要实用程序都在标准中实现。 如果您不使用 C++11 - 然后切换到 C++11 或更高版本 - 在不幸的情况下您不能,然后使用具有相同功能的 Boost

基本上,std::condition_variable 涵盖了您想使用此功能执行的操作。您可以使用函数 wait(它接受离开等待所必需的条件函数)、wait_forwait_until(与 wait 相同,但具有总等待时间限制)以及 notify_onenotify_all 唤醒休眠线程(一个或所有)并让它们检查唤醒条件并继续执行任务的方法。

查看参考资料中的 std::conditional_variable。只要 google 它,您就会通过示例找到足够的信息。

如果您出于某种原因不信任 std::conditional_variable 实施,您仍然可以将其用于迷你等待和唤醒。

  • How can I improve my implementation for a thread delay or sleep, that can be cancelled by another task, and is more consistent?

    • 高精度休眠has been discussed here before. I have used a waitable timer approach similar to what's described here.
  • Can a sleeping AFX thread be terminated? (What happens when a main thread terminates a sleeping AFX thread?)

    • 我假设你的意思是用 TerminateThread 终止? AFX 线程只是标准 Windows 线程的包装器。他们没有什么特别或神奇的东西可以区分他们的行为。所以会发生 bunch of bad stuff
      • 如果目标线程拥有临界区,临界区将不会被释放。
      • 如果目标线程正在从堆中分配内存,则不会释放堆锁。
      • 如果目标线程在终止时正在执行某些 kernel32 调用,则线程进程的 kernel32 状态可能不一致。
      • 如果目标线程正在操纵共享 DLL 的全局状态,则 DLL 的状态可能会被破坏,从而影响 DLL 的其他用户。
    • 如果您有权访问应用程序的所有源代码,则永远不必这样做。
  • What happens when a main thread terminates a thread that has called WaitForSingleObject?