Как генерировать запросы с целевой скоростью "запросов / сек"?
Скажи, у меня есть цель x
запросов / сек, которые я хочу генерировать постоянно. Моя цель - запускать эти запросы примерно с одинаковым интервалом, а не просто генерировать x запросов, а затем ждать, пока не истечет 1 секунда, и повторять все это снова и снова. Я не делаю никаких предположений об этих запросах, некоторые могут занять намного больше времени, чем другие, поэтому мой поток планировщика не будет выполнять запросы (или ждать их завершения), а передать их пулу потоков достаточного размера.
Сейчас если x
в диапазоне сотен или меньше, я мог бы обойтись с.net Timer
с или Thread.Sleep
и проверка фактически прошедшего времени с помощью Stopwatch
,
Но если я хочу пойти на тысячи или десятки тысяч, я мог бы попробовать использовать таймер с высоким разрешением, чтобы сохранить мой примерно такой же интервальный подход. Но это (в большинстве сред программирования в обычной ОС) подразумевает некоторое количество ручного кодирования с ожиданием вращения и т. Д., И я не уверен, стоит ли идти по этому пути.
Расширяя первоначальный подход, я мог бы вместо этого использовать таймер для сна и сделать y
запросы на каждое событие таймера, отслеживание фактических запросов в секунду, выполненных этим, и точная настройка y
во время выполнения. Эффект находится где-то посередине между "положи все x запросов и подожди до истечения 1 секунды с момента запуска", чего я стараюсь не делать, и "жди более или менее ровно 1/x секунды перед началом следующего запроса".
Последнее кажется хорошим компромиссом, но есть ли что-нибудь более простое, хотя распределение запросов с течением времени происходит довольно равномерно? Должно быть, это были реализованы сотни раз разными людьми, но я не могу найти хорошие ссылки по этому вопросу.
Так какой самый простой способ реализовать это?
1 ответ
Один из способов сделать это:
Сначала найти (удачи в Windows) или реализовать usleep
или же nanosleep
функция. В качестве первого шага это может быть (на.net) простой Thread.SpinWait()
/ Stopwatch.Elapsed > x
комбо. Если вы хотите, чтобы полюбить, сделать Thread.Sleep()
если промежуток времени достаточно велик и выполняйте только точную настройку, используя Thread.SpinWait()
,
Для этого просто возьмите обратную величину и у вас будет временной интервал, в течение которого вы должны спать между каждым событием. Ваш основной цикл, который вы делаете на одном выделенном потоке, а затем идет
- Пожарное событие
- Сон (время сна)
Затем каждые, скажем, 250 мс (или более для более высоких скоростей), проверяют фактически достигнутую скорость и корректируют sleepTime
интервал, возможно, с некоторым сглаживанием, чтобы ослабить дикие временные колебания, как это
newRate = max(1, sleepTime / targetRate * actualRate)
sleepTime = 0.3 * sleepTime + 0.7 * newRate
Это подстраивается под то, что на самом деле происходит в вашей программе и в вашей системе, и компенсирует время, затрачиваемое на вызов обратного вызова события, и все, что делает обратный вызов в том же потоке и т. Д. Без этого вы, вероятно, не сможете чтобы получить высокую точность.
Само собой разумеется, если ваша ставка настолько высока, что вы не можете использовать Sleep
но всегда нужно вращаться, одно ядро будет вращаться непрерывно. Хорошая новость: мы получаем все больше ядер на наших машинах, поэтому одно ядро имеет все меньшее и меньшее значение:) Более серьезно, хотя, как вы упомянули в комментарии, если ваша программа действительно работает, у вашего генератора событий будет меньше времени (и потребуется) тратить циклы.
Проверьте https://github.com/EugenDueck/EventCannon для доказательства реализации концепции в.net. Он реализован примерно так, как описано выше, и выполнен в виде библиотеки, поэтому вы можете встроить его в свою программу, если используете.net.