it-swarm-tr.com

İş parçacığı Windows'ta bir milisaniyeden daha az uyku yapmak nasıl

Windows'ta Unix'te hiç karşılaşmadığım bir sorunum var. Bu, bir milisaniyeden daha az bir süre boyunca uykuya dalmanın bir yoludur. Unix'te, ihtiyaçlarınızı karşılamak için genellikle bir dizi seçeneğiniz vardır (uyku, uyku ve nano uyku). Ancak Windows'ta yalnızca Uyku / milisaniye ayrıntı derecesine sahip. 

Unix'te, oldukça basit olan bir mikrosaniye uyku oluşturmak için select system call işlevini kullanabilirim:

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

Aynısını Windows'ta nasıl başarabilirim?

50
Jorge Ferreira

Bu uyku fonksiyonlarının yanlış anlaşıldığını gösterir. Geçirdiğiniz parametre uyumak için a minimum zamandır. İpin tam olarak belirtilen süreden sonra uyanacağının garantisi yoktur. Aslında, iplikler hiç 'uyanma' değildir, fakat programlayıcı tarafından yürütülmek üzere seçilmiştir. Programlayıcı, bir ipliği etkinleştirmek için, özellikle o anda başka bir iplik hala etkinse, istenen uyku süresinden çok daha fazla beklemeyi seçebilir.

88
Joel Coehoorn

Joel'in dediği gibi, kısa bir süre için anlamlı bir şekilde 'uyuyamazsınız (yani, zamanlanmış işlemcinizi bırakabilirsiniz). Kısa bir süre ertelemek istiyorsanız, uygun şekilde yüksek çözünürlüklü bir zamanlayıcıyı (örneğin, 'performans zamanlayıcı'yı) tekrar tekrar kontrol edip yüksek öncelikli bir şeyin sizi önlemediğini umarak sık sık döndürmeniz gerekir.

Bu kadar kısa sürelerde doğru gecikmeleri gerçekten önemsiyorsanız, Windows kullanmamalısınız.

47
Will Dean

Winmm.lib'de bulunan yüksek çözünürlüklü zamanlayıcıları kullanın. Örnek için this 'e bakınız.

26
Joe Schneider

Evet, işletim sisteminizin zaman miktarını anlamanız gerekiyor. Windows'ta, zaman miktarını 1 ms olarak değiştirmezseniz, 1 ms çözünürlük zamanları bile alamazsınız. (Örneğin, timeBeginPeriod ()/timeEndPeriod () kullanarak) Bu hala hiçbir şey garanti etmez. Küçük bir yük veya tek bir berbat aygıt sürücüsü bile her şeyi mahveder.

SetThreadPriority () yardımcı olur, ancak oldukça tehlikelidir. Kötü aygıt sürücüleri hala sizi mahvedebilir.

Bu çirkin şeylerin çalışabilmesi için ultra kontrollü bir bilgisayar ortamına ihtiyacınız var.

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

evet bazı belgelenmemiş çekirdek işlevlerini kullanıyor, ancak çok iyi çalışıyor, SleepShort (0.5) kullanıyorum; bazı şeylerimde

8
Oskar Dahlberg

Çok fazla ayrıntı düzeyi istiyorsanız, yanlış yerdesiniz (kullanıcı alanında). 

Kullanıcı alanındaysanız, zamanınızın her zaman kesin olmadığını unutmayın. 

Zamanlayıcı, iş parçanızı (veya uygulamanızı) başlatabilir ve zamanlayabilir, böylece işletim sistemi zamanlayıcısına bağlı olursunuz. 

Kesin bir şey arıyorsanız, gitmelisiniz: 1) Çekirdek alanında (sürücüler gibi) 2) Bir RTOS seçin.

Her neyse, biraz ayrıntı düzeyi arıyorsanız (ancak kullanıcı alanıyla ilgili sorunu hatırlayın) MSDN'deki QueryPerformanceCounter İşlevine ve QueryPerformanceFrequency işlevine bakın.

6
user16523

Birkaç kişinin işaret ettiği gibi, uyku ve diğer ilgili işlevler varsayılan olarak "sistem keneğine" bağlıdır. Bu, OS görevleri arasındaki minimum zaman birimidir; Örneğin, zamanlayıcı bundan daha hızlı çalışmayacaktır. Gerçek zamanlı bir işletim sistemiyle bile, sistem tıklaması genellikle 1 ms'den az değildir. Ayarlanabilir olmasına rağmen, yalnızca uyku işleviniz için değil, tüm sistem için çıkarımlar vardır, çünkü programlayıcınız daha sık çalışacaktır ve işletim sisteminizin ek yükünü artırabilir (zamanlayıcının çalışması için süre, vs. Bir görevin çalıştığı süre).

Bunun çözümü harici, yüksek hızlı bir saat cihazı kullanmaktır. Çoğu Unix sistemi, zamanlayıcılarınızı ve varsayılan sistem saatinin aksine, kullanmak için farklı bir saati belirtmenize izin verir.

5
mbyrne215

Genellikle bir uyku en azından bir sonraki sistem kesintisi gerçekleşene kadar devam eder. Bununla birlikte, bu Multimedya zamanlayıcı kaynaklarının ayarlarına bağlıdır. Bu, 1, 1 ms'ye yakın bir değere ayarlanmış olabilir, hatta bazı donanımlar 0.9765625 (ActualResolutionNtQueryTimerResolution tarafından sağlanan kesme sürelerinde çalışmasına bile izin verir. ActualResolution biçimine bir sayıdır (saniyede 1024 kesinlikte 0,9765625ms).

Kesinti süresinden daha kısa bir süre uyumak mümkün olmadığı gerçeğinden kaçmamıza izin veren bir istisna vardır: Bu ünlü Sleep(0). Bu çok güçlü bir Aracıdır ve olması gerektiği kadar kullanılmaz! İplik zaman diliminin hatırlatmasını feshetir. Bu şekilde, iş parçacığı, zamanlayıcı iş parçacığını tekrar cpu hizmeti alması için zorlayana kadar durur. Sleep(0) zaman uyumsuz bir hizmettir, çağrı, zamanlayıcıyı bir kesmeden bağımsız olarak tepki vermeye zorlar.

İkinci yol ise waitable object kullanımıdır. WaitForSingleObject() gibi bir wait işlevi bir olayı bekleyebilir. Bir iş parçacığının herhangi bir zamanda, aynı zamanda mikrosaniye rejiminde defalarca uyuyabilmesi için, iş parçacığının istenen gecikmede bir olay oluşturacak olan bazı servis ipliklerinin kurulması gerekmektedir. "Uyuyan" iş parçacığı bu iş parçacığını kuracak ve sonra hizmet iş parçacığı olay sinyalini verene kadar bekleme işlevinde duraklatılacaktır.

Bu şekilde, herhangi bir iplik "uyuyabilir" veya herhangi bir zaman bekleyebilir. Servis ipliği çok karmaşık olabilir ve mikrosaniye çözünürlükte zamanlanmış olaylar gibi sistem genelinde servisler sunabilir. Bununla birlikte, mikrosaniye çözünürlük, servis ipliğini en fazla bir kesme süresi boyunca (~ 1ms) yüksek çözünürlüklü bir süre servisinde dönmeye zorlayabilir. Dikkatli olunursa, bu özellikle çok işlemcili veya çok çekirdekli sistemlerde çok iyi olabilir. Bir ms dönüşü, çağıran iplik ve servis dişi için afinite maskesi dikkatle kullanıldığında, çok çekirdekli sistemde ciddi şekilde zarar görmez.

Kod, açıklama ve testler Windows Timestamp Project adresinde ziyaret edilebilir.

4
Arno

Bunun için böyle bir hassasiyet gerektiren şey nedir? Genel olarak eğer ihtiyaç duyarsanız bu hassasiyet seviyesini belirtmeniz gerekiyorsa (örneğin, bazı harici donanıma bağımlılık nedeniyle) yanlış platformdasınız ve gerçek zamanlı bir işletim sistemine bakmalısınız.

Aksi halde, senkronize edebileceğiniz bir olay olup olmadığını veya en kötüsü sadece meşgul olan CPU'yu bekleyin ve geçen süreyi ölçmek için yüksek performanslı sayıcı API kullanıp kullanmadığınızı düşünmelisiniz.

4
Rob Walker

Aslında bu uyku işlevinin kullanılması büyük bir bellek/kaynak sızıntısına neden olacaktır. (ne sıklıkta çağırıldığına bağlı olarak)

bu düzeltilmiş sürümü kullan (üzgünüz düzenleme yapamaz mı?)

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

Aynı problemim var ve hiçbir şey bir ms'den daha hızlı görünmüyor, hatta Sleep (0). Benim sorunum, bir istemci ile bir sunucu uygulaması arasındaki iletişimi, _InterlockedExchange işlevini biraz test edip ayarlamak için kullanıyorum ve ardından Uykum var (0).

Saniyede saniyede binlerce işlem yapmam gerekiyor ve planladığım kadar hızlı çalışmıyor.

Kullanıcıyla ilgilenen ince bir müşterim olduğu için, daha sonra bir iş parçacığıyla konuşan bir ajanı çağırır, bu durumda, iş parçacığıyla aracıyı birleştirmek için kısa sürede harekete geçeceğim, böylece olay arabirimi gerekli olmaz.

Sadece size bu Uykunun ne kadar yavaş olduğu hakkında bir fikir vermek için, 10 saniye boyunca boş bir döngü (18.000.000 döngü gibi bir şey elde etmek) yapmak için bir test yaptım, oysa olay yerinde sadece 180.000 döngü elde ettim. Yani, 100 kat daha yavaş!

2
Celso Bressan

Herkesin dediği gibi, uyku zamanı hakkında hiçbir garanti yoktur .Ancak kimse boşta bir sistemde, uyku komutunun çok kesin olabileceğini kabul etmek istemez. Özellikle gıdıklamayan bir çekirdekle. Windows Vista'da ve Linux'ta 2.6.16'dan beri vardır.

Dizüstü bilgisayarların pil ömrünün artmasına yardımcı olmak için tickless taneleri mevcuttur: c.f. Intel'in powertop yardımcı programı.

Bu durumda, istenen uyku zamanına çok fazla saygı duyan Linux'un uyku komutunu yarım düzine mikro saniyeye kadar ölçtüm.

Öyleyse, belki OP çoğu zaman boşta bir sistemde kabaca çalışacak bir şey ister ve mikro ikinci zamanlama isteyebilir! Aslında ben de bunu Windows'ta isterdim.

Ayrıca Uyku (0), terminolojinin daha net olduğu, boost :: thread :: verim () gibi geliyor.

Acaba Boost - zamanlamalı kilitlerin daha iyi bir hassasiyeti var mı? Çünkü o zaman hiç kimsenin bırakmadığı bir muteksi kilitleyebilirsiniz ve zaman aşımına ulaşıldığında devam edin .... __ Zaman aşımları boost :: system_time + boost :: milliseconds & cie ile ayarlanır (xtime kullanımdan kaldırılmıştır).

1
Lightness1024

SetWaitableTimer ... komutunu kullanmayı deneyin.

0
andrewrk

Boost :: xtime ve bir timed_wait () işlevini deneyin

nanosaniye doğruluğuna sahiptir.

0
theschmitzer

Sadece Sleep (0) kullanın. 0 açıkça bir milisaniyeden az. Şimdi, kulağa komik geliyor, ama ben ciddiyim. Uyku (0), Windows'a şu anda yapacak bir şeyinizin olmadığını, ancak zamanlayıcı yeniden çalışır çalışmaz yeniden ele almak istediğinizi söyler. Açıkça görüldüğü üzere, iş parçacığı zamanlayıcının kendisi çalışmadan önce çalıştırılmak üzere programlanamadığından, bu mümkün olan en kısa gecikmedir.

Kullandığınız mikrosaniye bir sayıyı uykuya aktaracağınızı unutmayın, ancak usleep geçersiz kılar (__ int64 t) {Uyku (t/1000); } - o dönemde uyumanın garantisi yok.

0
MSalters

Amacınız "çok kısa bir süre bekleyin" ise, spinwait yaptığınız için, o zaman gerçekleştirebileceğiniz bekleme düzeylerinde artış olur.

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

Eğer amacınız aslında sadece biraz beklemekse, şöyle bir şey kullanabilirsiniz:

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

Buradaki erdem, her seferinde daha uzun bekleyeceğimiz, sonunda tamamen uyuyacağımızdır. 

0
Ian Boyd

Uyku işlevi milisaniyeden daha az olan belki

Bu uykuyu (0) benim için çalıştığını gördüm. Görev yöneticisinde CPU üzerinde% 0'a yakın bir yüke sahip bir sistemde basit bir konsol programı yazdım ve uyku (0) işlevi, bir milisaniyeden çok daha az olan tutarlı bir 1-3 mikrosaniye boyunca uyudu.

Ancak bu konu başlığındaki yukarıdaki cevaplardan, uyku miktarının (0) uyuduğunun, büyük cpu yükü olan sistemlerde bundan çok daha çılgınca değişebileceğini biliyorum.

Ama anladığım kadarıyla uyku işlevi zamanlayıcı olarak kullanılmamalıdır. Programın işlemcinin en düşük yüzdesini kullanabilmesi ve mümkün olduğunca sık çalıştırılması için kullanılmalıdır. Amaçlarım gibi bir mermiyi ekran boyunca bir video oyununda bir pikselden çok daha hızlı hareket ettirmek, uyku (0) çalışır sanırım.

Uyku aralığının, uyuyacağı en büyük zaman miktarından çok daha küçük olduğundan emin olmalısınız. Uykuyu zamanlayıcı olarak kullanmazsınız, sadece oyunun mümkün olan en düşük cpu yüzdesini kullanmasını sağlamak için. Yapacak hiçbir şeyi olmayan, belirli bir sürenin ne zaman geçtiğini bilmek için uykuya daldırmak için ayrı bir işlevi kullanır ve sonra mermiyi ekran boyunca bir piksel hareket ettirir - milisaniyenin 1/10'u veya 100 mikrosaniyelik bir sürede .

Sözde kodu böyle bir şey giderdi.

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

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

//Rest of code in iteration here

Cevabın gelişmiş sorunlar veya programlar için çalışmayabileceğini, ancak bazı veya birçok program için işe yarayabileceğini biliyorum. 

0
rauprog