Приостановить поток менее одной миллисекунды
В клиентском тестовом приложении для обмена сообщениями поток производителя должен быть дросселирован, чтобы избежать наводнения сервера.
Поскольку скорость передачи составляет около 25 000 сообщений в секунду (40 микросекунд на сообщение), задержка, вызванная Sleep (1), слишком длинная.
Как сделать поток сна менее миллисекунды в Windows содержит некоторую информацию, связанную с Windows API. Есть ли фрагмент кода, класс или библиотека для Delphi?
После ответа Bens я обнаружил, что Sleep с разными значениями ниже 15 также дает разные скорости передачи (Windows Vista):
Сон (1) после каждых 20 сообщений:
00:02 tx/rx 25740/3705 12831/1846 msgs/sec (77/541 microsecs/msg)
00:04 tx/rx 53101/7405 13255/1848 msgs/sec (75/541 microsecs/msg)
00:06 tx/rx 79640/11288 13260/1879 msgs/sec (75/532 microsecs/msg)
00:08 tx/rx 104520/14562 13055/1818 msgs/sec (76/550 microsecs/msg)
00:10 tx/rx 130760/18829 13066/1881 msgs/sec (76/531 microsecs/msg)
Сон (5) после каждых 20 сообщений:
00:02 tx/rx 7640/3622 3812/1807 msgs/sec (262/553 microsecs/msg)
00:04 tx/rx 14660/10794 3661/2695 msgs/sec (273/371 microsecs/msg)
00:06 tx/rx 21480/18171 3577/3026 msgs/sec (279/330 microsecs/msg)
00:08 tx/rx 28140/25642 3515/3203 msgs/sec (284/312 microsecs/msg)
00:10 tx/rx 34980/32692 3496/3267 msgs/sec (286/306 microsecs/msg)
Это было неожиданно после прочтения комментария о нижнем пределе ожидания занятости.
И значения без дросселирования
00:02 tx/rx 44065/494 21988/246 msgs/sec (45/4065 microsecs/msg)
00:04 tx/rx 90493/756 22595/188 msgs/sec (44/5319 microsecs/msg)
00:06 tx/rx 142982/907 23810/151 msgs/sec (41/6622 microsecs/msg)
00:08 tx/rx 192562/1144 24055/142 msgs/sec (41/7042 microsecs/msg)
00:10 tx/rx 237294/1395 23717/139 msgs/sec (42/7194 microsecs/msg)
Ответы
Ответ 1
Отправить 20 сообщений, а затем спать в течение 1 миллисекунды?
Вы не можете спать меньше, чем квант планировщика, если у вас нет аппаратного прерывания, кроме системного таймера. Прочитайте ответы в вопросе, который вы связали, они объясняют, почему предлагаемый подход на самом деле не работает.
Даже при таком подходе вы можете спать дольше 1 мс, или поскольку сообщения не отправляются мгновенно, вся операция, безусловно, занимает больше 1 мс, уменьшая общую скорость.
Итак, проверяйте источник точности часов каждый раз, когда вы просыпаетесь, и вычисляете, сколько сообщений отправлять по истечении прошедшего времени, не используйте константу 20.
Ответ 2
1) Получить текущее время.
2) Рассчитайте, сколько сообщений вам нужно отправить, исходя из того, сколько вы отправили и сколько времени прошло с тех пор.
3) Отправить много сообщений.
4) Сон за минимальную сумму, которую вы можете.
5) Перейдите к шагу 1.
Ответ 3
Как говорили другие, вы не можете спать в течение короткого промежутка времени (и даже Sleep (1) не является надежным - он может легко спать в течение более 1 мс).
Лучше всего рассчитать время для следующего сообщения, а затем выполнить ожидание цикла ожидания и проверить время - до тех пор, пока не произойдет требуемое время. Ниже представлено полное решение вместе с небольшой тестовой платформой.
program SendEquidistantMessages;
{$APPTYPE CONSOLE}
{$R *.res}
uses
Windows, SysUtils;
procedure SendMessage(msgNum: integer);
begin
// send the message here
end;
procedure WaitUntil(nextMsgTime: int64);
var
currTime: int64;
begin
repeat
QueryPerformanceCounter(currTime);
if currTime >= nextMsgTime then
break; //repeat
asm pause; end;
until false;
end;
procedure SendMessages(numMsg, msgPerSec: integer);
var
iMsg : integer;
nextMsgTime: int64;
perfFreq : int64;
prevMsg : int64;
startTime : int64;
begin
Assert(QueryPerformanceFrequency(perfFreq));
Assert(QueryPerformanceCounter(startTime));
for iMsg := 1 to numMsg do begin
WaitUntil(Round(startTime + iMsg/msgPerSec * perfFreq));
SendMessage(iMsg);
end;
end;
var
time: cardinal;
begin
try
time := GetTickCount;
SendMessages(20000, 5000);
time := GetTickCount-time;
Writeln('20.000 messages sent in ', time/1000:4:1, ' sec; ',
'required rate = 5000 msg/sec, real rate = ', 20000/(time/1000):6:1, ' msg/sec');
Readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Ответ 4
Вместо того, чтобы спать, почему бы не использовать T Theard.Yield, чтобы другой поток/процесс имел bash в процессоре?
Ответ 5
Существует поток Sleep Less Than One Millisecond, посвященный аналогичным проблемам.
Я дал несколько подробностей о том, как позволить потоку спать в любое время. Это, конечно, также включает в себя сон в микросекундах.
Темы: Временные события, созданные потоками службы, функции ожидания
Более подробную информацию можно найти в Проект временной отметки Windows
Ответ 6
обходным путем является получение текущего времени с помощью nano секунд и добавление его со временем, когда вы хотите спать (ждать) и цикл до тех пор, пока текущее время (с nano секунд) не станет больше, чем вы рассчитали, существует способ которые иллюстрируют это:
public void wait(long nano){
long t= System.nanoTime();
t+= nano;
while(System.nanoTime()<t);
}
Обратите внимание, что:
1 секунда = 1000 миллисекунд = 1000000 микросекунд = 1000000000 наносекунд
так
1 миллисекунда = 1000000 наносекунд