it-swarm-id.com

Cara membuat thread sleep kurang dari satu milidetik di Windows

Di Windows saya punya masalah yang tidak pernah saya temui di Unix. Begitulah cara membuat utas tidur kurang dari satu milidetik. Di Unix, Anda biasanya memiliki sejumlah pilihan (tidur, tidur, dan tidur sebentar) untuk memenuhi kebutuhan Anda. Pada Windows, bagaimanapun, hanya ada Sleep dengan granularity milidetik. 

Di Unix, saya bisa menggunakan panggilan sistem select untuk membuat sleep mikrodetik yang cukup mudah:

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

Bagaimana saya bisa mencapai hal yang sama di Windows?

50
Jorge Ferreira

Ini menandakan salah paham fungsi tidur. Parameter yang Anda lewati adalah minimum waktu untuk tidur. Tidak ada jaminan bahwa utas akan bangun setelah tepat waktu yang ditentukan. Sebenarnya, utas sama sekali tidak 'bangun', tetapi lebih dipilih untuk dieksekusi oleh penjadwal. Penjadwal mungkin memilih untuk menunggu lebih lama daripada durasi tidur yang diminta untuk mengaktifkan utas, terutama jika utas lain masih aktif pada saat itu.

88
Joel Coehoorn

Seperti yang dikatakan Joel, Anda tidak bisa 'tidur' dengan bermakna (yaitu melepaskan CPU terjadwal) untuk periode yang singkat. Jika Anda ingin menunda untuk waktu yang singkat, maka Anda perlu memutar, berulang kali memeriksa timer resolusi tinggi yang sesuai (mis. 'Timer kinerja') dan berharap sesuatu yang prioritas tinggi tidak mengganggu Anda terlebih dahulu.

Jika Anda benar-benar peduli tentang keterlambatan akurat dalam waktu sesingkat itu, Anda seharusnya tidak menggunakan Windows.

47
Will Dean

Gunakan timer resolusi tinggi yang tersedia di winmm.lib. Lihat ini untuk contoh.

26
Joe Schneider

Ya, Anda perlu memahami kuantum waktu OS Anda. Di Windows, Anda bahkan tidak akan mendapatkan resolusi 1 ms kali kecuali Anda mengubah kuantum waktu menjadi 1 ms. (Menggunakan misalnya timeBeginPeriod ()/timeEndPeriod ()) Itu masih tidak benar-benar menjamin apa pun. Bahkan sedikit beban atau satu driver perangkat jelek akan membuang semuanya.

SetThreadPriority () membantu, tetapi cukup berbahaya. Driver perangkat yang buruk masih dapat merusak Anda.

Anda membutuhkan lingkungan komputasi yang sangat terkontrol untuk membuat hal-hal buruk ini bekerja sama sekali.

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);
}

ya itu menggunakan beberapa fungsi kernel tidak berdokumen, tetapi berfungsi dengan sangat baik, saya menggunakan SleepShort (0,5); di beberapa threds saya

8
Oskar Dahlberg

Jika Anda ingin granularity begitu banyak Anda berada di tempat yang salah (di ruang pengguna). 

Ingatlah bahwa jika Anda berada di ruang pengguna, waktu Anda tidak selalu tepat. 

Penjadwal dapat memulai utas Anda (atau aplikasi), dan menjadwalkannya, sehingga Anda bergantung pada penjadwal OS. 

Jika Anda mencari sesuatu yang tepat Anda harus pergi: 1) Dalam ruang kernel (seperti driver) 2) Pilih RTOS.

Lagi pula jika Anda mencari beberapa rincian (tapi ingat masalah dengan ruang pengguna) lihat Fungsi QueryPerformanceCounter dan fungsi QueryPerformanceFrequency di MSDN.

6
user16523

Seperti yang ditunjukkan oleh beberapa orang, fungsi sleep dan fungsi terkait lainnya secara default bergantung pada "centang sistem". Ini adalah satuan minimum waktu antara tugas-tugas OS; scheduler, misalnya, tidak akan berjalan lebih cepat dari ini. Bahkan dengan OS waktu nyata, centang sistem biasanya tidak kurang dari 1 ms. Sementara itu merdu, ini memiliki implikasi untuk seluruh sistem, bukan hanya fungsi tidur Anda, karena penjadwal Anda akan berjalan lebih sering, dan berpotensi meningkatkan overhead OS Anda (jumlah waktu untuk penjadwal untuk berjalan, vs. jumlah waktu tugas dapat dijalankan).

Solusi untuk ini adalah dengan menggunakan perangkat jam eksternal berkecepatan tinggi. Sebagian besar sistem Unix akan memungkinkan Anda untuk menentukan timer Anda dan jam yang berbeda untuk digunakan, sebagai lawan dari jam sistem default.

5
mbyrne215

Apa yang Anda tunggu yang membutuhkan ketelitian seperti itu? Secara umum jika Anda perlu untuk menentukan tingkat presisi tersebut (mis. Karena ketergantungan pada beberapa perangkat keras eksternal) Anda berada di platform yang salah dan harus melihat OS waktu nyata.

Kalau tidak, Anda harus mempertimbangkan apakah ada acara yang dapat Anda sinkronkan, atau dalam kasus yang lebih buruk, hanya sibuk menunggu CPU dan menggunakan penghitung API kinerja tinggi untuk mengukur waktu yang berlalu.

4
Rob Walker

Sebenarnya menggunakan fungsi tidur ini akan menyebabkan memori besar/sumber daya bocor. (tergantung seberapa sering dipanggil)

gunakan versi yang diperbaiki ini (maaf tidak bisa mengedit?)

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

Saya memiliki masalah yang sama dan sepertinya tidak ada yang lebih cepat daripada ms, bahkan Sleep (0). Masalah saya adalah komunikasi antara klien dan aplikasi server di mana saya menggunakan fungsi _InterlockedExchange untuk menguji dan mengatur sedikit dan kemudian saya Tidur (0).

Saya benar-benar perlu melakukan ribuan operasi per detik dengan cara ini dan itu tidak berfungsi secepat yang saya rencanakan.

Karena saya memiliki klien tipis yang berurusan dengan pengguna, yang pada gilirannya meminta agen yang kemudian berbicara ke utas, saya akan segera bergerak untuk menggabungkan utas dengan agen sehingga tidak ada antarmuka acara yang diperlukan.

Hanya untuk memberi kalian ide betapa lambatnya Tidur ini, saya menjalankan tes selama 10 detik melakukan putaran kosong (mendapatkan sekitar 18.000.000 loop) sedangkan dengan acara di tempat saya hanya mendapat 180.000 loop. Yaitu, 100 kali lebih lambat!

2
Celso Bressan

Seperti semua orang yang disebutkan, memang tidak ada jaminan tentang waktu tidur . Tapi tidak ada yang mau mengakui bahwa kadang-kadang, pada sistem idle, perintah usleep bisa sangat tepat. Apalagi dengan kernel yang tidak berdetak. Windows Vista memilikinya dan Linux memilikinya sejak 2.6.16.

Kernel tanpa tick ada untuk membantu meningkatkan kehidupan batterly laptop: c.f. Utilitas powertop Intel.

Dalam kondisi itu, saya dengan senang hati telah mengukur perintah usleep Linux yang sangat menghormati waktu tidur yang diminta, hingga setengah lusin detik mikro.

Jadi, mungkin OP menginginkan sesuatu yang kira-kira akan bekerja sebagian besar waktu pada sistem idling, dan dapat meminta penjadwalan detik mikro! ... Saya benar-benar ingin itu pada Windows juga.

Sleep (0) juga terdengar seperti boost :: thread :: yield (), yang terminologinya lebih jelas.

Saya ingin tahu apakah Tingkatkan - kunci waktunya memiliki presisi yang lebih baik. Karena dengan begitu Anda bisa mengunci pada mutex yang tidak pernah dilepaskan oleh siapa pun, dan ketika batas waktu tercapai, lanjutkan ... Timeout ditetapkan dengan boost :: system_time + boost :: milidetik & cie (xtime sudah usang).

1
Lightness1024

Coba gunakan SetWaitableTimer ...

0
andrewrk

Coba boost :: xtime dan timed_wait ()

memiliki akurasi nanosecond.

0
theschmitzer

Cukup gunakan Tidur (0). 0 jelas kurang dari satu milidetik. Sekarang, itu terdengar lucu, tapi saya serius. Sleep (0) memberi tahu Windows bahwa Anda tidak memiliki sesuatu untuk dilakukan saat ini, tetapi Anda ingin dipertimbangkan kembali segera setelah penjadwal berjalan kembali. Dan karena jelas utas tidak dapat dijadwalkan untuk berjalan sebelum penjadwal itu sendiri berjalan, ini adalah penundaan sesingkat mungkin.

Perhatikan bahwa Anda dapat memasukkan nomor mikrodetik ke ruang tidur Anda, tetapi demikian juga membatalkan ruang tidur (__ int64 t) {Tidur (t/1000); } - tidak ada jaminan untuk benar-benar tidur selama periode itu.

0
MSalters

Jika tujuan Anda adalah "menunggu dalam waktu yang sangat singkat" karena Anda melakukan spinwait , maka ada peningkatan level menunggu yang dapat Anda lakukan.

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;
   }
}

Jadi, jika tujuan Anda sebenarnya untuk menunggu hanya untuk sedikit , Anda dapat menggunakan sesuatu seperti:

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

Keutamaan di sini adalah bahwa kita menunggu lebih lama setiap kali, akhirnya tidur sepenuhnya. 

0
Ian Boyd

Fungsi tidur yang kurang dari satu milidetik-mungkin

Saya menemukan bahwa tidur (0) bekerja untuk saya. Pada sistem dengan beban hampir 0% pada cpu di task manager, saya menulis program konsol sederhana dan fungsi sleep (0) tidur selama 1-3 mikrodetik yang konsisten, yang jauh lebih kecil dari satu milidetik.

Tetapi dari jawaban di atas dalam utas ini, saya tahu bahwa jumlah sleep (0) sleep dapat bervariasi jauh lebih liar daripada ini pada sistem dengan beban cpu yang besar.

Tapi seperti yang saya pahami, fungsi tidur seharusnya tidak digunakan sebagai penghitung waktu. Ini harus digunakan untuk membuat program menggunakan persentase CPU sesedikit mungkin dan mengeksekusi sesering mungkin. Untuk tujuan saya, seperti memindahkan proyektil melintasi layar dalam videogame jauh lebih cepat dari satu pixel per milidetik, sleep (0) berfungsi, saya pikir.

Anda hanya perlu memastikan bahwa interval tidurnya jauh lebih kecil daripada jumlah waktu tidur terbesarnya. Anda tidak menggunakan tidur sebagai penghitung waktu tetapi hanya untuk membuat game menggunakan jumlah minimum cpu yang dimungkinkan. Anda akan menggunakan fungsi terpisah yang tidak ada hubungannya adalah tidur untuk mengetahui kapan waktu tertentu telah berlalu dan kemudian memindahkan proyektil satu piksel melintasi layar - pada saat mengatakan 1/10 dari milidetik atau 100 mikrodetik .

Pseudo-code akan seperti ini.

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

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

//Rest of code in iteration here

Saya tahu jawabannya mungkin tidak berfungsi untuk masalah atau program lanjutan tetapi mungkin berfungsi untuk beberapa atau banyak program. 

0
rauprog