Newsgroup: it.comp.lang.c++
Titolo: Eseguire thread ad intervalli regolari

Premetto che sono solo pochi giorni che mi sto rompendo la testa con il VC5.0.
Qualcuno sa se e' possibile, una volta avviato un thread, riuscire nell'intento di
eseguirlo ad intervalli REGOLARI brevi (es. 10ms) e PRECISI? Insomma, tipo quello che
si faceva in DOS andando ad intercettare l'interrupt del timer di sistema?
Io ho provato a cambiare le priorita' del processo e dei thread, ma non sono riuscito
ad ottenere nulla che abbia un errore inferiore ai 30 msec.
So benissimo che Win95 non e' un s.o. real time, ma ci deve essere un modo...
Grazie
Pablon
--
Eliminare 'TOGLI' dall'indirizzo per rispondermi!

ad ottenere nulla che abbia un errore inferiore ai 30 msec.
in win95 non puoi ottenere temporizzazioni precise al di sotto dei
55ms e anche in NT arrivi al massimo a 10ms. Comunque per ottenere
quanto ti serve dovrebbe essere sufficiente utilizzare i multimedia
timers che permettono anche risoluzioni inferiori. Con la
timeGetDevCaps() puoi sapere la risoluzione minima e massima
ottenibile, con timeBeginPeriod puoi impostare la risoluzione minima e
infine con la timeSetEvent puoi impostare il timer e far chiamare una
tua funzione.
Gianpaolo Bottin
- Live Long and Prosper -

timers che permettono anche risoluzioni inferiori. Con la
Giusto.  La precisione non e` comunque granche`, ma, "grosso modo",
per certe applicazioni non troppo stringenti, potrebbe anche bastare.
Ecco un piccolo "scheletro" di programmino che puo` servire a verificare
questi fattori -- buildare multi-threading e aggiungendo la winmm.lib,
naturalmente, e come console application:
#define STRICT
#include <stdio.h>
#include <windows.h>
#include <mmsystem.h>
//
// istogramma dei tempi in mSec corrispondenti a un certo
// ritardo periodico nominale
//
const int msMin = 0;    // 1o bin dell'istogramma = msMin o meno ms
const int msMax = 24;   // ultimo bin " " = msMax-1 o piu` ms
int hist[msMax-msMin];  // l'istogramma
const int msNom = 10;   // ritardo periodico nominale (in mSec)
int nHits = 0;                      // quante ripetizioni sinora
const int maxHits = 4*1000/msNom;   // run per 4 secondi in tutto
__int64 ticksPerSecond; // unita` di misura del Performance Ctr
__int64 roundingOffset;
__int64 tpsLatest;      // istante ultimo evento (in ticksPerSecond)
//
// function che viene eseguita periodicamente
//
void periodic()
{
    LARGE_INTEGER liCurrent;
    QueryPerformanceCounter(&liCurrent);
    __int64 tpsCurrent;
    memcpy(&tpsCurrent, &liCurrent, sizeof(__int64));
    if(nHits) {  // non e` la prima volta, registriamo in istogramma
        __int64 delta   = tpsCurrent-tpsLatest+roundingOffset;
        int msDelta     = int((1000*delta)/ticksPerSecond);
        int histIndex;
        if(msDelta<=msMin)
            histIndex = 0;
        else if(msDelta>=msMax)
            histIndex = msMax-msMin-1;
        else
            histIndex = msDelta-msMin;
        ++hist[histIndex];
    }
    tpsLatest = tpsCurrent;
    ++nHits;
}
//
//  gestione del ciclo di esecuzione periodica e sua terminazione
//
void CALLBACK mainloop(UINT, UINT, DWORD dwSync, DWORD, DWORD)
{
    if(nHits < maxHits) {
        periodic();
    } else {
        SetEvent((HANDLE)dwSync);
    }
}
//
// programma principale
//
int main()
{
    LARGE_INTEGER liTicksPerSecond;
    if(!QueryPerformanceFrequency(&liTicksPerSecond)) {
        printf("Questo sistema non supporta le misure di prestazioni\n");
        return 1;
    }
    memcpy(&ticksPerSecond, &liTicksPerSecond, sizeof(__int64));
    printf("Frequenza di misurazione = %d tick al secondo\n",
        int(ticksPerSecond));
    roundingOffset = ticksPerSecond/2000;
    TIMECAPS tc;
    MMRESULT mmResult = timeGetDevCaps(&tc, sizeof(tc));
    printf("Risoluzioni dei timer MM: da %d a %d mSec\n",
        tc.wPeriodMin, tc.wPeriodMax);
    HANDLE gSync = CreateEvent(0,0,0,0);
    MMRESULT mmTimerId = timeSetEvent(msNom, 0,
        mainloop, (DWORD)gSync, TIME_PERIODIC);
    DWORD dwWr = WaitForSingleObject(gSync, 2*msNom*maxHits);
    printf("Exit rc -> %x\n", dwWr-WAIT_OBJECT_0);
    timeKillEvent(mmTimerId);
    for(int i=msMin; i<msMax; ++i)
        printf("%2d -> %6d\n", i, hist[i-msMin]);
    return 0;
}
La simulazione puo` essere personalizzata, e resa un modello
piu` accurato delle proprie specifiche esigenze, cambiando, nel
main(), la semplice WaitForSingleObject con un opportuno ciclo
che svolge il tipo di compiti che dovranno essere poi in effetti
svolti dal nostro programma -- ad esempio, se il nostro programma
non sara` una console  application, qui si potrebbe avere un normale
loop degli eventi (appoggiato su MsgWaitForMultipleObjects, o con
altri tipi di sincronizzazione fra thread principale e secondario, ad
esempio una PostMessage dal thread secondario al principale per
avvertire della fine del ciclo).
Comunque, anche nel caso piu` favorevole (la WaitForSingleObject,
che non impone al sistema nessun carico, e a sistema totalmente a
riposo), risultati abbastanza tipici sono (test su Win98, Cyrix 686/166;
tre run immediatamente successivi, macchina "a riposo"; rimosse le
righe d'istogramma con 0 occorrenze):
 0 ->     10
 7 ->      1
 9 ->      9
10 ->    366
11 ->     11
23 ->      2
 9 ->     16
10 ->    366
11 ->     17
 9 ->      2
10 ->    394
11 ->      3
Notiamo che, al primo run, l'istogramma e` decisamente piu`
"sparpagliato" -- migliora al secondo, concentrandosi attorno
al desiderato tempo di 10 mSec ma con "code" ancora assai
sostanziali -- e` quasi accettabile al terzo run.  Probabilmente
questo riflette irregolarita` del comportamento di memoria
virtuale e cache, che vanno sparendo quando esse sono gia`,
tendenzialmente, "caricate" col codice e i dati del nostro
programmino di esempio.
Alex