3

我需要在 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
...

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

4

2 回答 2

3

再说unpark()一个线程的能力,LockSupport.parkNanos(...)无非就是一个睡眠。在 Windows 上的 OpenJDK Hotspot VM 中,它使用实现(第 4436 行)WaitForSingleObject(...),并且休眠至少 1ms。

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

注释Sleep()指出,您可以通过调用timeBeginPeriod().

于 2012-08-16T04:19:33.553 回答
2

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

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

于 2012-08-16T02:23:48.777 回答