it-swarm-vi.tech

Cách làm cho luồng ngủ ít hơn một phần nghìn giây trên Windows

Trên Windows tôi có một vấn đề tôi chưa bao giờ gặp phải trên Unix. Đó là làm thế nào để có được một chủ đề để ngủ ít hơn một phần nghìn giây. Trên Unix bạn thường có một số lựa chọn (ngủ, ngủ và ngủ nano) để phù hợp với nhu cầu của bạn. Tuy nhiên, trên Windows, chỉ có Ngủ với độ chi tiết mili giây. 

Trên Unix, tôi có thể sử dụng lệnh gọi hệ thống select để tạo một giấc ngủ micro giây khá đơn giản:

int usleep(long usec)
{
    struct timeval tv;
    tv.tv_sec = usec/1000000L;
    tv.tv_usec = usec%1000000L;
    return select(0, 0, 0, 0, &tv);
}

Làm thế nào tôi có thể đạt được điều tương tự trên Windows?

50
Jorge Ferreira

Điều này cho thấy sự hiểu lầm về chức năng giấc ngủ. Tham số bạn vượt qua là tối thiểu thời gian để ngủ. Không có gì đảm bảo rằng chuỗi sẽ thức dậy sau thời gian chính xác được chỉ định. Trên thực tế, các luồng không 'thức dậy' chút nào, mà được chọn bởi trình lập lịch biểu. Bộ lập lịch có thể chọn chờ lâu hơn thời lượng ngủ được yêu cầu để kích hoạt một luồng, đặc biệt nếu một luồng khác vẫn đang hoạt động tại thời điểm đó.

88
Joel Coehoorn

Như Joel nói, bạn không thể "ngủ" một cách có ý nghĩa (nghĩa là từ bỏ CPU đã lên lịch của bạn) trong những khoảng thời gian ngắn như vậy. Nếu bạn muốn trì hoãn trong một thời gian ngắn, thì bạn cần phải quay, liên tục kiểm tra bộ hẹn giờ có độ phân giải cao phù hợp (ví dụ: 'bộ đếm thời gian hiệu suất') và hy vọng rằng thứ gì đó có mức độ ưu tiên cao sẽ không làm bạn thất vọng.

Nếu bạn thực sự quan tâm đến sự chậm trễ chính xác của những khoảng thời gian ngắn như vậy, bạn không nên sử dụng Windows.

47
Will Dean

Sử dụng bộ định thời độ phân giải cao có sẵn trong winmm.lib. Xem này cho một ví dụ.

26
Joe Schneider

Có, bạn cần hiểu lượng tử thời gian của hệ điều hành. Trên Windows, bạn thậm chí sẽ không nhận được độ phân giải 1ms trừ khi bạn thay đổi lượng tử thời gian thành 1ms. (Sử dụng ví dụ timeBeginPeriod ()/timeEndPeriod ()) Điều đó vẫn không thực sự đảm bảo bất cứ điều gì. Ngay cả một chút tải hoặc một trình điều khiển thiết bị crappy duy nhất sẽ ném mọi thứ đi.

SetThreadP Warriority () giúp, nhưng khá nguy hiểm. Trình điều khiển thiết bị xấu vẫn có thể hủy hoại bạn.

Bạn cần một môi trường điện toán cực kỳ kiểm soát để làm cho công cụ xấu xí này hoạt động.

9
darron
#include <Windows.h>

static NTSTATUS(__stdcall *NtDelayExecution)(BOOL Alertable, PLARGE_INTEGER DelayInterval) = (NTSTATUS(__stdcall*)(BOOL, PLARGE_INTEGER)) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtDelayExecution");

static NTSTATUS(__stdcall *ZwSetTimerResolution)(IN ULONG RequestedResolution, IN BOOLEAN Set, OUT PULONG ActualResolution) = (NTSTATUS(__stdcall*)(ULONG, BOOLEAN, PULONG)) GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwSetTimerResolution");




static void SleepShort(float milliseconds) {
    static bool once = true;
    if (once) {
        ULONG actualResolution;
        ZwSetTimerResolution(1, true, &actualResolution);
        once = false;
    }

    LARGE_INTEGER interval;
    interval.QuadPart = -1 * (int)(milliseconds * 10000.0f);
    NtDelayExecution(false, &interval);
}

vâng, nó sử dụng một số hàm kernel không có giấy tờ, nhưng nó hoạt động rất tốt, tôi sử dụng SleepShort (0,5); trong một số thứ của tôi

8
Oskar Dahlberg

Nếu bạn muốn quá nhiều chi tiết, bạn đang ở sai vị trí (trong không gian người dùng). 

Hãy nhớ rằng nếu bạn ở trong không gian người dùng, thời gian của bạn không phải lúc nào cũng chính xác. 

Bộ lập lịch có thể bắt đầu chuỗi (hoặc ứng dụng) của bạn và lên lịch cho nó, do đó bạn phụ thuộc vào bộ lập lịch của hệ điều hành. 

Nếu bạn đang tìm kiếm thứ gì đó chính xác, bạn phải đi: 1) Trong không gian kernel (như trình điều khiển) 2) Chọn RTOS.

Dù sao, nếu bạn đang tìm kiếm một số mức độ chi tiết (nhưng hãy nhớ vấn đề với không gian người dùng) hãy tìm đến Hàm QueryPerformanceCorer và Hàm QueryPerformanceFrequency trong MSDN.

6
user16523

Như nhiều người đã chỉ ra, giấc ngủ và các chức năng liên quan khác theo mặc định phụ thuộc vào "đánh dấu hệ thống". Đây là đơn vị thời gian tối thiểu giữa các tác vụ HĐH; bộ lập lịch, chẳng hạn, sẽ không chạy nhanh hơn cái này. Ngay cả với một hệ điều hành thời gian thực, đánh dấu hệ thống thường không dưới 1 ms. Mặc dù có thể điều chỉnh, nhưng điều này có ý nghĩa đối với toàn bộ hệ thống, không chỉ là chức năng ngủ của bạn, bởi vì trình lập lịch biểu của bạn sẽ chạy thường xuyên hơn và có khả năng tăng chi phí cho hệ điều hành của bạn (lượng thời gian để trình lập lịch chạy, so với lượng thời gian một nhiệm vụ có thể chạy).

Giải pháp cho vấn đề này là sử dụng một thiết bị đồng hồ tốc độ cao bên ngoài. Hầu hết các hệ thống Unix sẽ cho phép bạn chỉ định cho bộ định thời của mình và sử dụng đồng hồ khác như vậy, trái ngược với đồng hồ hệ thống mặc định.

5
mbyrne215

Nói chung một giấc ngủ sẽ kéo dài ít nhất cho đến khi gián đoạn hệ thống tiếp theo xảy ra. Tuy nhiên, điều này phụ thuộc vào cài đặt của tài nguyên bộ đếm thời gian đa phương tiện. Nó có thể được đặt ở mức gần 1 ms, một số phần cứng thậm chí cho phép chạy ở các khoảng thời gian gián đoạn 0,9765625 ( ActualResolution được cung cấp bởi NtQueryTimerResolution sẽ hiển thị 0.9766 nhưng điều đó thực sự sai. Họ chỉ không thể đặt số chính xác vào định dạng ActualResolution . Đó là 0.9765625ms với 1024 ngắt mỗi giây).

Có một ngoại lệ cho phép chúng ta thoát khỏi thực tế là có thể không thể ngủ ít hơn thời gian gián đoạn: Đó là Sleep(0) nổi tiếng. Đây là một công cụ rất mạnh mẽ và nó không được sử dụng thường xuyên như vậy! Nó từ bỏ lời nhắc nhở về lát cắt thời gian của chuỗi. Bằng cách này, luồng sẽ dừng cho đến khi bộ lập lịch buộc luồng để nhận lại dịch vụ cpu. Sleep(0) là một dịch vụ không đồng bộ, cuộc gọi sẽ buộc bộ lập lịch phản ứng độc lập với ngắt.

Cách thứ hai là sử dụng waitable object. Hàm chờ như WaitForSingleObject() có thể đợi một sự kiện. Để có một luồng ngủ bất cứ lúc nào, cũng có lần trong chế độ micro giây, luồng cần thiết lập một số luồng dịch vụ sẽ tạo ra một sự kiện ở độ trễ mong muốn. Chuỗi "ngủ" sẽ thiết lập luồng này và sau đó tạm dừng tại chức năng chờ cho đến khi luồng dịch vụ sẽ đặt tín hiệu sự kiện.

Bằng cách này, bất kỳ chủ đề có thể "ngủ" hoặc chờ đợi bất cứ lúc nào. Chuỗi dịch vụ có thể rất phức tạp và nó có thể cung cấp các dịch vụ toàn hệ thống như các sự kiện được định thời ở độ phân giải micro giây. Tuy nhiên, độ phân giải micro giây có thể buộc luồng dịch vụ quay trên dịch vụ thời gian có độ phân giải cao trong tối đa một khoảng thời gian gián đoạn (~ 1ms). Nếu cẩn thận, điều này có thể chạy rất tốt, đặc biệt trên các hệ thống đa bộ xử lý hoặc đa lõi. Một vòng quay một ms không ảnh hưởng đáng kể đến hệ thống đa lõi, khi mặt nạ ái lực cho luồng cuộc gọi và luồng dịch vụ được xử lý cẩn thận.

Mã, mô tả và kiểm tra có thể được truy cập tại Dự án dấu thời gian của Windows

4
Arno

Bạn còn chờ gì nữa mà đòi hỏi sự chính xác như vậy? Nói chung nếu bạn cần để chỉ định mức độ chính xác đó (ví dụ: do phụ thuộc vào một số phần cứng bên ngoài), bạn đang sử dụng sai nền tảng và nên xem xét hệ điều hành thời gian thực.

Mặt khác, bạn nên xem xét nếu có một sự kiện bạn có thể đồng bộ hóa hoặc trong trường hợp xấu hơn chỉ cần chờ CPU và sử dụng API bộ đếm hiệu suất cao để đo thời gian đã trôi qua.

4
Rob Walker

Trên thực tế sử dụng chức năng ngủ này sẽ gây rò rỉ bộ nhớ/tài nguyên lớn. (tùy thuộc vào mức độ thường được gọi)

sử dụng phiên bản sửa lỗi này (xin lỗi không thể chỉnh sửa?)

bool usleep(unsigned long usec)
{
    struct timeval tv;
    fd_set dummy;
    SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    FD_ZERO(&dummy);
    FD_SET(s, &dummy);
    tv.tv_sec = usec / 1000000ul;
    tv.tv_usec = usec % 1000000ul;
    bool success = (0 == select(0, 0, 0, &dummy, &tv));
    closesocket(s);
    return success;
}
2
Hendrik

Tôi có cùng một vấn đề và dường như không có gì nhanh hơn một ms, ngay cả Ngủ (0). Vấn đề của tôi là giao tiếp giữa máy khách và ứng dụng máy chủ nơi tôi sử dụng hàm _InterlockedExchange để kiểm tra và thiết lập một chút và sau đó tôi ngủ (0).

Tôi thực sự cần phải thực hiện hàng ngàn thao tác mỗi giây theo cách này và nó không hoạt động nhanh như tôi dự định.

Vì tôi có một máy khách mỏng giao dịch với người dùng, do đó sẽ gọi một tác nhân sau đó nói chuyện với một luồng, tôi sẽ sớm di chuyển để hợp nhất luồng với tác nhân để không yêu cầu giao diện sự kiện.

Chỉ để cung cấp cho các bạn một ý tưởng về giấc ngủ này chậm như thế nào, tôi đã chạy thử nghiệm trong 10 giây để thực hiện một vòng lặp trống (nhận được khoảng 18.000.000 vòng lặp) trong khi với sự kiện tại chỗ tôi chỉ có 180.000 vòng lặp. Đó là, chậm hơn 100 lần!

2
Celso Bressan

Giống như mọi người đã đề cập, thực sự không có gì đảm bảo về thời gian ngủ . Nhưng không ai muốn thừa nhận rằng đôi khi, trên một hệ thống nhàn rỗi, lệnh ngủ có thể rất chính xác. Đặc biệt với một kernel tickless. Windows Vista có nó và Linux có nó từ 2.6.16.

Hạt nhân vô tận tồn tại để giúp cải thiện cuộc sống của máy tính xách tay: c.f. Tiện ích powertop của Intel.

Trong điều kiện đó, tôi rất vui vì đã đo được lệnh ngủ của Linux, rất tôn trọng thời gian ngủ được yêu cầu rất chặt chẽ, xuống còn nửa tá micro giây.

Vì vậy, có lẽ OP muốn một cái gì đó gần như sẽ hoạt động hầu hết thời gian trên một hệ thống chạy không tải và có thể yêu cầu lập lịch vi mô thứ hai! Tôi thực sự cũng muốn điều đó trên Windows.

Ngoài ra Ngủ (0) nghe có vẻ như boost :: thread :: ield (), thuật ngữ này rõ ràng hơn.

Tôi tự hỏi nếu Boost - khóa thời gian có độ chính xác tốt hơn. Bởi vì sau đó bạn chỉ có thể khóa trên một mutex mà không ai từng phát hành, và khi hết thời gian chờ, hãy tiếp tục ... Hết giờ được đặt với boost :: system_time + boost :: milliseconds & cie (xtime bị phản đối).

1
Lightness1024

Hãy thử sử dụng SetWaitableTimer ...

0
andrewrk

Hãy thử boost :: xtime và timed_wait ()

có độ chính xác nano giây.

0
theschmitzer

Chỉ cần sử dụng Ngủ (0). 0 rõ ràng là ít hơn một phần nghìn giây. Bây giờ, điều đó nghe có vẻ buồn cười, nhưng tôi nghiêm túc. Ngủ (0) cho Windows biết rằng bạn không có việc gì để làm ngay bây giờ, nhưng bạn muốn được xem xét lại ngay khi trình lập lịch chạy lại. Và vì rõ ràng luồng không thể được lên lịch để chạy trước khi trình lập lịch biểu chạy, đây là độ trễ ngắn nhất có thể.

Lưu ý rằng bạn có thể chuyển một số micrô giây cho giấc ngủ của mình, nhưng cũng không có hiệu lực để ngủ (__ int64 t) {Ngủ (t/1000); } - không đảm bảo để thực sự ngủ trong khoảng thời gian đó.

0
MSalters

Nếu mục tiêu của bạn là "chờ trong một khoảng thời gian rất ngắn" bởi vì bạn đang thực hiện spinwait , thì sẽ có các mức độ chờ đợi tăng lên mà bạn có thể thực hiện.

void SpinOnce(ref Int32 spin)
{
   /*
      SpinOnce is called each time we need to wait. 
      But the action it takes depends on how many times we've been spinning:

      1..12 spins: spin 2..4096 cycles
      12..32: call SwitchToThread (allow another thread ready to go on time core to execute)
      over 32 spins: Sleep(0) (give up the remainder of our timeslice to any other thread ready to run, also allows APC and I/O callbacks)
   */
   spin += 1;

   if (spin > 32)
      Sleep(0); //give up the remainder of our timeslice
   else if (spin > 12)
      SwitchTothread(); //allow another thread on our CPU to have the remainder of our timeslice
   else
   {
      int loops = (1 << spin); //1..12 ==> 2..4096
      while (loops > 0)
         loops -= 1;
   }
}

Vì vậy, nếu mục tiêu của bạn thực sự là chờ đợi chỉ trong một chút , bạn có thể sử dụng một cái gì đó như:

int spin = 0;
while (!TryAcquireLock()) 
{ 
   SpinOne(ref spin);
}

Đức tính ở đây là chúng ta chờ đợi lâu hơn mỗi lần, cuối cùng sẽ hoàn toàn đi ngủ. 

0
Ian Boyd

Hàm ngủ ít hơn một phần nghìn giây - có thể

Tôi thấy rằng giấc ngủ (0) làm việc cho tôi. Trên một hệ thống có tải gần 0% trên cpu trong trình quản lý tác vụ, tôi đã viết một chương trình điều khiển đơn giản và chức năng ngủ (0) đã ngủ trong 1-3 giây, tức là chưa đến một phần nghìn giây.

Nhưng từ các câu trả lời ở trên trong chủ đề này, tôi biết rằng số lượng giấc ngủ (0) ngủ có thể khác nhau nhiều hơn so với điều này trên các hệ thống có tải cpu lớn.

Nhưng theo tôi hiểu, chức năng ngủ không nên được sử dụng như một bộ đếm thời gian. Nó nên được sử dụng để làm cho chương trình sử dụng tỷ lệ phần trăm ít nhất của cpu càng tốt và thực thi càng thường xuyên càng tốt. Đối với mục đích của tôi, chẳng hạn như di chuyển một viên đạn trên màn hình trong trò chơi điện tử nhanh hơn nhiều so với một pixel một mili giây, ngủ (0) hoạt động, tôi nghĩ vậy.

Bạn sẽ chắc chắn rằng khoảng thời gian ngủ nhỏ hơn khoảng thời gian lớn nhất mà nó sẽ ngủ. Bạn không sử dụng chế độ ngủ như một bộ đếm thời gian mà chỉ để làm cho trò chơi sử dụng số phần trăm cpu tối thiểu có thể. Bạn sẽ sử dụng một chức năng riêng biệt không có gì để làm là ngủ để biết khi nào một khoảng thời gian cụ thể trôi qua và sau đó di chuyển một pixel trên màn hình - tại thời điểm nói là 1/10 của một phần nghìn giây hoặc 100 micro giây .

Mã giả sẽ đi một cái gì đó như thế này.

while (timer1 < 100 microseconds) {
sleep(0);
}

if (timer2 >=100 microseconds) {
move projectile one pixel
}

//Rest of code in iteration here

Tôi biết câu trả lời có thể không hoạt động cho các vấn đề hoặc chương trình nâng cao nhưng có thể hoạt động cho một số hoặc nhiều chương trình. 

0
rauprog