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 держит __int64
s. У меня есть одна нить продюсера и одна нить потребителя. Поток получателя также порождает поток таймера, который каждую секунду опрашивает получателя на предмет порядкового номера события, которое он в последний раз использовал. Наступает момент, когда Потребитель не продвигается, а Продюсер еще не закончил. Продюсер просто запускает цикл несколько сотен миллионов раз, публикуя счетчик. Итак, мой вывод выглядит примерно так:
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 и поэтому должен быть доступен для загрузки или просмотра.