【问题标题】:Windows C++ equivalent of Java's LockSupport.parkNanos()Windows C++ 等效于 Java 的 LockSupport.parkNanos()
【发布时间】:2012-08-16 01:45:37
【问题描述】:

我需要在 Win7 x64 上实现与此功能相同的功能。

我最初使用SwitchToThread() 但这不起作用,因为它在极端条件下会导致死锁。我能找到的唯一选择是Sleep(),但这很可能会成为性能杀手,因为它只适用于毫秒分辨率,我仍然不确定它是否与LockSupport.parkNanos() 做同样的事情。

我发现 Java 以纳秒间隔调度(如果发生这种情况)线程的能力很可疑,因此我实现了我只能假设它们会做的事情......旋转。但是我不确定这是否能解决问题,它可能只是延迟不可避免的事情,因为 Java 函数似乎需要 JVM 的干预才能工作。 parkNanos 没有可用的源代码;它在原生 Sun 库中实现。

class LockSupport
{
public:
    static void ParkNanos(unsigned __int64 aNanos)
    {
        ULONGLONG start;
        ULONGLONG end;

        ::QueryUnbiasedInterruptTime(&start);
        do
        {
            // My issue with this is that nothing is actually 'Parked'.
            ::SwitchToThread();
            ::QueryUnbiasedInterruptTime(&end);
        }
        while ((end - start) < aNanos);
    }
};

调用代码如下:

void SomeClass::SomeFunction()
{
    while (someCond)
    {
        LockSupport.parkNanos(1L);
    }
}

FWIW,我正在将 LMAX 的 Disruptor 模式移植到 C++。当一个线程在SingleThreadedClaimStrategy::WaitForFreeSlotAt() 而另一个线程在BlockingWaitStrategy::WaitFor(没有超时)时,就会发生死锁。当 RingBuffer 的大小很小时,死锁会更加明显...... 1、2、4、8 等。

线程是通过正常的CreateThread 方式创建的。

编辑:我写这篇文章的时候已经很晚了,所以这里有更多信息。 RingBuffer 保存__int64s。我有一个生产者线程和一个消费者线程。 Consumer 线程还生成一个 Timer 线程,该线程每秒轮询 Consumer 以获取它上次消费的事件的序列号。当消费者没有进展并且生产者也没有完成时,就会出现这样的情况。 Producer 只是在循环中运行了几亿次,发布了一个计数器。所以我的输出看起来像这样:

898
97
131
Timer: no progress
Timer: no progress
...

只有在发布模式下才能真正重现,所有内容都针对速度进行了优化。

【问题讨论】:

  • 你说你和::SwitchToThread()陷入僵局。是返回真还是假?
  • @Managu 在正常执行期间它可以返回两者。然而,你的评论让我想到了在这个函数返回 false 时旋转这个函数。
  • 您如何实现 ReentrantLock 和 Condition,即 BlockingWaitStrategy 使用的?你的锁实现是可重入的吗?条件变量是否原子地获取/释放相应的锁?
  • 你有没有在合适的地方插入内存屏障? Java 计算模型明确设置了关于volatile 原语的读写障碍,但 C++ 没有这样做。阅读有关 Disruptor 的更多信息,内存屏障看起来很重要。
  • @Managu 我已将尽可能多的 Java 代码复制到其 Windows 等效代码中。谢谢你的关注。我在这里问过 LMAX 的人:groups.google.com/forum/?fromgroups#!topic/lmax-disruptor/…

标签: java c++ concurrency disruptor-pattern


【解决方案1】:

除了unpark() 一个线程的能力之外,LockSupport.parkNanos(...) 只不过是一个睡眠。在 Windows 上的 OpenJDK Hotspot VM 中,它是 implemented(第 4436 行)使用 WaitForSingleObject(...),并且至少休眠 1ms。

LMAX 干扰器似乎从来没有出现在unpark() 线程中。因此,您应该通过调用Sleep(1) 来获得等效的行为。使用Sleep(0) 可能会做得更好:您放弃当前线程中剩余的时间片,并立即可以重新安排。这相当于SwitchToThread(),除了后者可能只是告诉你“还没有准备好运行,所以你可以保留cpu”。另一方面,如果您的调度粒度足够低,Sleep(1) 实际上可能会暂停 1 毫秒。

remarksSleep() 中指出,您可以通过调用timeBeginPeriod() 来提高系统的调度粒度(可能低至每tick 1 毫秒)。

【讨论】:

    【解决方案2】:

    parkNanos 没有可用的源代码;它在原生 Sun 库中实现。

    该本地库的源代码应该是 OpenJDK 6 / 7 源代码的一部分,因此应该可以下载或浏览。

    【讨论】:

    猜你喜欢
    • 2016-01-07
    • 2012-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-03
    • 2010-11-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多