暂停线程不到一毫秒

时间:2012-01-14 07:25:07

标签: multithreading delphi messaging sleep

在消息传递客户端测试应用程序中,需要限制生产者线程以避免泛滥服务器。

由于传输速率约为每秒25,000条消息(每条消息40微秒),因此睡眠(1)引起的延迟时间太长。

How to make thread sleep less than a millisecond on Windows包含一些与Windows API相关的信息。是否有Delphi的代码片段,类或库?


按照Bens的回答,我发现不同值低于15的睡眠也会提供不同的传输速率(Windows Vista):

每20条消息后休眠(1):

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)
每隔20条消息后

睡眠(5):

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)

6 个答案:

答案 0 :(得分:21)

发送20条消息,然后睡眠1毫秒?

除非您有系统计时器以外的硬件中断,否则您的睡眠时间不能超过调度程序量程。阅读您链接的问题中的答案,他们解释了为什么建议的方法实际上不起作用。

即使使用这种方法,您的睡眠时间可能超过1毫秒,或者由于消息不会立即发送,整个操作肯定会花费超过1毫秒的时间,从而降低总体速率。

因此,每次唤醒时检查精确时钟源,并根据经过的时间计算要发送的消息数量,不要使用常量20。

答案 1 :(得分:12)

1)获取当前时间。

2)根据您发送的数量以及自那时以来经过的时间计算您需要发送的邮件数量。

3)发送那么多消息。

4)尽可能少地睡觉。

5)转到第1步。

答案 2 :(得分:3)

正如其他人所说,你不能在短时间内睡觉(甚至睡眠(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.

答案 3 :(得分:0)

为什么不使用TTheard.Yield让另一个线程/进程在处理器上有bash而不是睡觉呢?

答案 4 :(得分:0)

有一个线程Sleep Less Than One Millisecond处理类似的问题。 我已经给出了一些关于如何让线程随时睡眠的细节。这当然还包括微秒范围内的睡眠。 主题:服务线程创建的定时事件,等待函数

更多细节也可以在Windows Timestamp Project

找到

答案 5 :(得分:0)

解决方法是以纳秒为单位获取当前时间并将其添加到您想要睡眠的时间(等待)并循环直到当前时间(纳秒)变得大于您计算的时间,有一种方法这说明了这一点:

public void wait(long nano){
    long t= System.nanoTime();
    t+= nano;
    while(System.nanoTime()<t);
}

请注意: 1秒= 1000毫秒= 1000000微秒= 1000000000纳秒

所以

1毫秒= 1000000纳秒