【问题标题】:Critical section queue临界区队列
【发布时间】:2013-03-26 04:41:47
【问题描述】:

Windows临界区没有队列的概念吗?

我在专用线程中有以下渲染循环:

while (!viewer->finish)
{
  EnterCriticalSection(&viewer->lock);
  viewer->renderer->begin();
  viewer->root->render(viewer->renderer);
  viewer->renderer->end();
  LeaveCriticalSection(&viewer->lock);
}

主线程进行消息处理,当我处理鼠标事件时,我尝试进入相同的临界区,但由于某种原因,它在主线程之前运行渲染线程一千多次迭代(大约 10 秒)线程终于进入临界区。是什么导致了这个 - 即使没有“队列”进入该部分,它不应该更像 50/50,而不是像我的情况那样的 99.9/0.1?两个线程的优先级均为 0。

添加此类队列的好方法是什么?像 bDoNotRenderAnything 这样的简单标志就足够了吗?

编辑:在我的情况下,解决方案只是添加一个事件对象(一个布尔变量也可能工作),每次消息处理程序需要访问关键部分时设置它,并在使用它后重置。如果设置了变量/事件,则渲染器不会进入该部分。这样消息处理程序就不必等待多次渲染迭代。

【问题讨论】:

  • 为什么viewer->finish 不受并发访问保护?它至少是不稳定的吗??
  • 您的while 循环在解锁后立即锁定,这没有帮助。
  • 主线程和渲染线程的优先级是什么?
  • 因为 viewer->finish 只被主线程修改。优先级都为零。我认为可能是这种情况,但它不应该至少在再次进入临界区之前检查其他线程是否试图承担控制权吗?

标签: c++ winapi critical-section


【解决方案1】:

在旧版本的 Windows 中,保证以先到先得的方式获取关键部分。从 Windows Server 2003 SP1 开始不再是这种情况。

来自MSDN:

从带有 Service Pack 1 (SP1) 的 Windows Server 2003 开始​​,在临界区等待的线程不再以先到先得的方式获取临界区。 此更改提高了性能对于大多数代码来说意义重大。但是,某些应用程序依赖于先进先出 (FIFO) 顺序,并且可能在当前版本的 Windows 上表现不佳或根本不执行(例如,一直使用临界区作为速率限制器的应用程序)。为确保您的代码继续正常工作,您可能需要添加额外的同步级别。例如,假设您有一个生产者线程和一个消费者线程,它们使用临界区对象来同步他们的工作。创建两个事件对象,每个线程使用一个来表示它已准备好让另一个线程继续进行。消费者线程在进入临界区之前将等待生产者发出其事件信号,而生产者线程将在进入临界区之前等待消费者线程发出其事件信号。在每个线程离开临界区后,它会发出信号来释放另一个线程。

Windows Server 2003 和 Windows XP: 在临界区等待的线程被添加到等待队列;它们被唤醒并通常按照它们被添加到队列中的顺序获取关键部分。但是,如果以足够快的速度将线程添加到此队列中,则性能可能会因为唤醒每个等待线程所需的时间而降低。

【讨论】:

  • 好的,这是我想避免的,但谢谢。 MSDN 中的If all the calling threads have the same priority, a first-in, first-out (FIFO) basis is used after the current thread leaves the critical section. 行让我很困惑。
  • @riv:我用谷歌搜索了这句话,我看到它的唯一地方是Windows Mobile 6.5。如果您的问题是关于 Windows Mobile,那么您应该这样说。
  • 是的,那是我的错误,我习惯于在 Visual Studio 中内置 MSDN,现在我没有它,我只是用谷歌搜索我需要的任何功能,并不总是注意到不寻常的平台。
【解决方案2】:

在临界区等待的线程不会以先到先得的方式获取临界区 (MSDN)

大多数时候您的工作线程拥有锁,因为它在释放锁后立即重新锁定。所以没有太多时间让其他线程在空闲时唤醒并抓住锁。

【讨论】:

    【解决方案3】:

    根据MSDN

    There is no guarantee about the order in which waiting threads 
    will acquire ownership of the critical section.
    

    所以不确定线程​​将按什么顺序执行。如果你比较短

    viewer->renderer->begin();
    viewer->root->render(viewer->renderer);
    viewer->renderer->end();
    

    序列设法重新获得CriticalSection 结束,这可能会发生。

    【讨论】:

    • 哎呀,我才意识到我一直在看 Windows Mobile MSDN =(
    【解决方案4】:

    您可以尝试在渲染循环中使用SwitchToThread 调用来快速修复(经过一定次数的迭代),尽管我怀疑它是否足够好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-17
      • 2013-04-07
      • 2021-09-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多