2012-01-14 41 views
14

Trong ứng dụng thử nghiệm ứng dụng khách nhắn tin, chuỗi sản xuất cần phải được điều chỉnh để tránh làm ngập máy chủ.Tạm dừng một chuỗi trong ít hơn một mili giây

Do tốc độ truyền tải khoảng 25.000 tin nhắn mỗi giây (40 micro giây cho mỗi tin nhắn), độ trễ gây ra bởi Sleep (1) sẽ quá dài.

How to make thread sleep less than a millisecond on Windows chứa một số thông tin liên quan đến API Windows. Có một đoạn mã, lớp hoặc thư viện cho Delphi?


Sau Bens câu trả lời tôi thấy rằng giấc ngủ với các giá trị khác nhau dưới 15 cũng cho tốc độ khác nhau chuyển (Windows Vista):

Sleep (1) sau mỗi 20 tin nhắn:

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) 

Ngủ (5) sau mỗi 20 tin nhắn:

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) 

Điều này bất ngờ sau khi đọc nhận xét ab ra giới hạn dưới cho bận rộn chờ đợi

Và các giá trị mà không throttling

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) 
+0

Bạn có thể thực hiện chỉ bận rộn chờ đợi ít hơn 1 ms, không ngủ. – kludg

+0

@Serg, AFAIK khoảng 15 ms – OnTheFly

Trả lời

21

Gửi 20 tin nhắn và sau đó ngủ 1 mili giây?

Bạn không thể ngủ ít hơn so với lượng tử của trình lên lịch, trừ khi bạn có phần cứng ngắt khác với hẹn giờ hệ thống. Đọc câu trả lời trong câu hỏi bạn đã liên kết, họ giải thích lý do tại sao phương pháp được đề xuất không thực sự hiệu quả. Ngay cả với cách tiếp cận này, bạn có thể ngủ lâu hơn 1ms, hoặc kể từ khi tin nhắn không được gửi ngay lập tức, toàn bộ hoạt động chắc chắn sẽ mất nhiều hơn 1ms, giảm tỷ lệ tổng thể.

Vì vậy, kiểm tra một nguồn đồng hồ chính xác mỗi khi bạn thức, và tính toán bao nhiêu tin nhắn để gửi dựa trên thời gian trôi qua, không sử dụng một hằng số 20.

+3

Trên Windows, tôi khuyên bạn nên sử dụng QueryPerformanceCounter (http://msdn.microsoft.com/en-us/library/windows/desktop/ms644904(v=vs.85).aspx) làm độ chính xác nguồn đồng hồ (xuống đến độ phân giải khoảng một nửa micro giây). – Crashworks

+0

+1, chắc chắn là cách tiếp cận tốt nhất - thiết kế đi sự cần thiết cho sự chậm trễ ngắn. –

12

1) Lấy thời gian hiện tại.

2) Tính số lượng thư bạn cần gửi dựa trên số lượng thư bạn đã gửi và thời gian đã trôi qua kể từ đó.

3) Gửi nhiều thư đó.

4) Ngủ với số tiền nhỏ nhất bạn có thể.

5) Đến bước 1.

0

Thay vì ngủ, tại sao không sử dụng TTheard.Yield để cho một thread/quá trình có một bash ở bộ vi xử lý?

+0

..có thể không có bất kỳ chủ đề nào khác đã sẵn sàng, trong trường hợp này, Lợi nhuận không có gì. –

3

Như những người khác đã nói, bạn không thể ngủ trong một khoảng thời gian ngắn (và ngay cả Sleep (1) không đáng tin cậy - nó có thể dễ dàng ngủ nhiều hơn 1 ms).

Đặt cược tốt nhất của bạn là tính toán thời gian cho thư tiếp theo và sau đó thực hiện thời gian chờ đợi - vòng lặp và thời gian kiểm tra bận - cho đến khi thời gian mong muốn xảy ra. Dưới đây là một giải pháp đầy đủ cùng với một khuôn khổ thử nghiệm nhỏ.

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. 
+1

Nếu có thể (với phiên bản gần đây của Delphi), tôi ủng hộ việc sử dụng 'Diagnostic.TStopWatch' để theo dõi thời gian trôi qua: nó cho phép truy cập vào bộ hẹn giờ có độ phân giải cao, nếu có. – menjaraz

+0

TStopWatch cũng tốt cho tương thích OS/X - điểm tốt! – gabr

0

Có một sợi Sleep Less Than One Millisecond đối phó với vấn đề tương tự. Tôi đã đưa ra một số chi tiết về cách để cho một chủ đề ngủ bất cứ lúc nào. Điều này tất nhiên cũng bao gồm giấc ngủ trong phạm vi micro giây. Chủ Đề: Sự kiện Timed tạo ra bởi chủ đề dịch vụ, Chờ chức năng

Thông tin chi tiết cũng có thể được tìm thấy tại Windows Timestamp Project

0

một workaround là để có được thời gian hiện tại với giây nano và bổ sung nó với thời gian bạn muốn ngủ (chờ đợi) và vòng lặp cho đến thời điểm hiện tại (với nano giây) trở nên lớn hơn thời gian bạn tính toán, có một phương pháp mà minh họa điều này:

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

Lưu ý rằng: 1 giây = 1.000 mili giây = 1000000 micro = 1000000000 nano giây

nên

1 mili giây = 1000000 nano giây

Các vấn đề liên quan