Windows C++ эквивалент JavaS LockSupport.parkNanos()

Мне нужно реализовать ту же функциональность, что и эта функция на 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, я портирую образец Disruptor LMAX на C++. Тупик происходит, когда один поток находится в SingleThreadedClaimStrategy::WaitForFreeSlotAt() а другой в BlockingWaitStrategy::WaitFor (без таймаута). Тупик становится более заметным, когда размер RingBuffer крошечный... 1, 2, 4, 8 и т. Д.

Темы создаются нормальными CreateThread средства.

Редактировать: было довольно поздно, когда я написал это, так что вот еще немного информации. RingBuffer держит __int64s. У меня есть одна нить продюсера и одна нить потребителя. Поток получателя также порождает поток таймера, который каждую секунду опрашивает получателя на предмет порядкового номера события, которое он в последний раз использовал. Наступает момент, когда Потребитель не продвигается, а Продюсер еще не закончил. Продюсер просто запускает цикл несколько сотен миллионов раз, публикуя счетчик. Итак, мой вывод выглядит примерно так:

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

Это действительно воспроизводимо только в Release Mode, все оптимизировано для скорости.

2 ответа

Решение

Помимо способности unpark() Тема, LockSupport.parkNanos(...) это не что иное, как сон. В виртуальной машине OpenJDK Hotspot в Windows она реализована (строка 4436) с использованием WaitForSingleObject(...) и спит минимум 1 мс.

Разрушитель LMAX никогда не кажется unpark() потоки. Следовательно, вы должны получить эквивалентное поведение, вызвав Sleep(1), Вы могли бы сделать лучше с Sleep(0): вы оставляете оставшуюся часть времени в текущем потоке и сразу становитесь доступными для перепланирования. Это эквивалентно SwitchToThread() за исключением того, что последний может просто сказать вам "ничего не готово к запуску, поэтому вы можете оставить процессор". С другой стороны, Sleep(1) может на самом деле сделать паузу на 1 мс, если уровень детализации вашего планирования достаточно низок.

В комментариях к Sleep() отмечается, что вы можете улучшить детализацию планирования вашей системы (возможно, до 1 мс на такт), вызывая timeBeginPeriod(),

Исходный код для parkNanos недоступен; это реализовано в родной библиотеке Sun.

Исходный код этой нативной библиотеки должен быть частью исходного кода OpenJDK 6 / 7 и поэтому должен быть доступен для загрузки или просмотра.

Другие вопросы по тегам