Announcement

Collapse
No announcement yet.

Thread loop speed with Sleep

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Michael Mattias
    replied
    >this version generates a wav file of the whole text and plays it through the speakers.

    Now THAT is *VERY* cool! Nice work!

    MCM

    PS:
    You might want to post that in the Source Code Forum with a 'searchable' title, eg "Generate and Play Morse Code WAV files from text input"
    Last edited by Michael Mattias; 4 Jul 2009, 08:25 AM.

    Leave a comment:


  • Michael Mattias
    replied
    > thought this thread was about "SLEEP" not Morse Code and only used it as an example

    Isn't it amazing that when you describe your application instead of/in addition to the function or statement you are currently try to deploy in that application, you end up with more and better suggestions?

    Leave a comment:


  • Paul Dixon
    replied
    Michael,
    this version generates a wav file of the whole text and plays it through the speakers.
    Code:
    'PBCC5 program
    #DIM ALL
    #BREAK ON
    #INCLUDE "win32api.inc"
    
    FUNCTION PBMAIN()
    
    MorseCode("SOS SOS Hello from Mister Morse SOS", 80,1000,50)  'What to send, dot duration, frequency and % volume
         
    PRINT "press key to end."
    WAITKEY$
    END FUNCTION
    
    
    'define types
    TYPE WAVEheader
    rID              AS STRING*4  ' Contains the characters "RIFF"
    rLen             AS LONG      ' The length of the data in the next chunk
    wID              AS STRING*4  ' Contains the characters "WAVE"
    fId              AS STRING*4  ' Contains the characters "fmt "
    fLen             AS LONG      ' Length of data in the format chunk
    wFormatTag       AS INTEGER   ' specifies the wave format, eg 1 = Pulse Code Modulation
    nChannels        AS INTEGER   ' Number of channels, 1=mono, 2=stereo
    nSamplesPerSec   AS LONG      ' Playback frequency
    nAvgBytesPerSec  AS LONG      ' Indicates the average number of bytes a second the data should be
                                  ' transferred at = nChannels * nSamplesPerSec * (nBitsPerSample / 8)
    nBlockAlign      AS INTEGER   ' Indicates the block alignment of the data in the data chunk. Software
                                  ' needs to process a multiplt of nBlockAlign at a time.
                                  ' nBlockAlign = nChannels * (nBitsPerSample / 8)
    wBitsPerSample   AS INTEGER   ' Format specific data area
    dId              AS STRING*4  ' Contains the characters "data"
    dLen             AS LONG      ' Length of data in the dData field
    END TYPE
    
    
    TYPE twominwav
         wv AS waveheader
         dta AS ASCIIZ *1000000
    END TYPE
    
    
    SUB MorseCode(Phrase AS STRING, DitDuration AS LONG, Freq AS LONG, Volume AS LONG)
    STATIC sound AS twominwav
    LOCAL ShortGap, LetterGap, WordGap, r, k AS LONG
    LOCAL DitSamples, DahSamples, TotalSamples, Char AS LONG
    LOCAL SCALE AS EXT
    LOCAL p AS BYTE PTR
    LOCAL symbol, letter, Morse AS STRING
    
    IF DitDuration > 330 THEN DitDuration = 330  'limit of 1 second for a dah
    
    DitSamples = 8000/1000 * DitDuration  '8000 samples/sec, 1000 msec/sec
    DahSamples = 3 * DitSamples
    ShortGap = DitSamples
    LetterGap = 2 * DitSamples  'should be 3*DitSamples but dot or dash always ends in a 1 Dit gap so only 2 more to add
    WordGap = 4 * DitSamples  'should be 7*DitSamples but the letter always ends in a 3 Dit gap so only 4 more to add
    
    SCALE=2*3.14159265358979323#/8000
      
    p=VARPTR(sound.dta)
    
    FOR char = 1 TO LEN(Phrase)
        
        letter = LCASE$(MID$(Phrase,char,1))
                
        SELECT CASE letter
            CASE "a" TO "z"
                symbol = READ$(ASC(letter)-ASC("a")+1)
                
            CASE "0" TO "9"
                symbol = READ$(ASC(letter)-ASC("0")+27)
    
            CASE ELSE
                symbol = " "
        END SELECT
            
                      
        FOR k = 1 TO LEN (symbol)
    
        'generate the tone samples
        SELECT CASE MID$(symbol,k,1)
            CASE "."
                FOR r = 0 TO DitSamples-1
                     @p[r]=volume*(SIN(r * SCALE * Freq) ) +128
                NEXT
                p = p + DitSamples
                TotalSamples = TotalSamples + DitSamples
    
                FOR r = 0 TO ShortGap-1
                     @p[r]= 128     'silence
                NEXT
                p = p + ShortGap
                TotalSamples = TotalSamples + ShortGap
    
            CASE "-"
                FOR r = 0 TO DahSamples-1
                     @p[r]=volume*(SIN(r * SCALE * Freq) ) +128
                NEXT
                p = p + DahSamples
                TotalSamples = TotalSamples + DahSamples
    
                FOR r = 0 TO ShortGap-1
                     @p[r]= 128     'silence
                NEXT
                p = p + DitSamples
                TotalSamples = TotalSamples + ShortGap
    
            CASE " "
                FOR r = 0 TO WordGap-1
                     @p[r]=128     'silence
                NEXT
                p = p + WordGap
                TotalSamples = TotalSamples + WordGap
    
            END SELECT
        
        NEXT
        
        FOR r = 0 TO LetterGap-1
             @p[r]=128     'silence
        NEXT
        p = p + LetterGap
            TotalSamples = TotalSamples + LetterGap
            
    NEXT
        
    'sort out the wav header
    sound.wv.rID             = "RIFF"
    sound.wv.rLen            = 36+TotalSamples
    sound.wv.wID             = "WAVE"
    sound.wv.fId             = "fmt "
    sound.wv.fLen            = 16
    sound.wv.wFormatTag      = 1
    sound.wv.nChannels       = 1 'nChannels
    sound.wv.nSamplesPerSec  = 8000 'nSamplesPerSec
    sound.wv.nAvgBytesPerSec = 8000  '44100  '88200 'nChannels * nSamplesPerSec * (wBitsPerSample / 8)
    sound.wv.nBlockAlign     = 1 ' nChannels * (wBitsPerSample / 8)
    sound.wv.wBitsPerSample  = 8 'wBitsPerSample
    sound.wv.dId             = "data"
    sound.wv.dLen            = TotalSamples
    
    
    playsound( BYVAL VARPTR(sound),0 ,%SND_MEMORY OR %SND_ASYNC )
    
         
    'a-m
    DATA .-,-...,-.-.,-..,.,..-.,--.,....,..,.---,-.-,.-..,--
    'n-z
    DATA -.,---,.--.,--.-,.-.,...,-,..-,...-,.--,-..-,-.--,--..
    '0-9
    DATA -----,.----,..---,...--,....-,.....,-....,--...,---..,----.
           
    END SUB

    Leave a comment:


  • Denys Brosnan
    replied
    I thought this thread was about "SLEEP" not Morse Code and only used it as an example. However not to be outdone I will have a look at your code Paul and make up my own mind after trying it out. Such variables as speed (from 25 to 250 letters per minute), ratio (varying the 3 dits to 1 dah timing) etc must come into it. Sound does not come into it as it must switch a radio transmitters output on and off for each dit or dah using either a COM port or a PARALLEL port. The arrangement I have works exceedingly well changing a character such as "a" to the necessary dit/dah combination. But I try out your code and see whether it gives any additional benefit.
    73 Denys

    Leave a comment:


  • Chris Boss
    replied
    Just because you can't guarantee the speed of the thread, does not mean that using a thread won't work for what you need.

    The first question is:

    "What task does the thread need to accomplish" ?

    Second, how accurate a time interval is required ?

    In many instances, as long as the thread runs at a reasonable pace, there may be no need to have absolute control of the speed.

    For example, lets say that one needs to poll a device or something at least 10 times a second. Thats 100 milliseconds.

    A thread would work reasonably well.

    The thread could run a continuous loop and use a QueryPerformanceCounter to get an accurate count for the frame rate (each iteration) to see how fast the routine is running (no matter how Windows handles time slices).

    Calculate how much time it took for the current iteration, since the last one using high performance timers and then determine whether the current iteration has taken took long or whether it has finished faster than what is needed. If the current iteration has taken longer than was is required, then simply don't call SLEEP and your thread will run as fast as is possible to keep up. If there is time left for the current iteration, then call SLEEP based on the calculation of how much time is left for the iteration (frame) so you can reliquish the time slice to other threads/processes.

    If the time slice for each iteration is not enough to keep up with the speed you need then you need to have some kind of skip iteration built in to compensate and then flag to the user and the program that the thread can not keep up.

    This is how it is done with video I think. With video in the old days if the thread could not keep up with the requested frame rate, the software would drop frames.

    Now if you can't keep up to the required frame rate, then you may need to increase the priority of the thread so Windows gives it more time for each time slice while it decreases the time given to other threads.

    This is why Windows allows you to set thread priority levels.

    Each process is given a specific thread priority and each thread within the process is given a thread priority. The SetThreadPriority API function for example lets you add or subtract from the current priority of a thread.

    There are very high priorities left for things like device drivers and hardware in the rare instance you need absolute control over the speed but that is beyond the scope of my knowledge and requires some special programming skills.

    Leave a comment:


  • John Gleason
    replied
    I went away to do a little programming, and now with the recent posts above, this is all old hat. It doesn't make real letters and words, but it sounds kinda kewl and seems pretty even timing-wise.
    Code:
    #COMPILE EXE
    #DIM ALL
    DECLARE FUNCTION sndPlaySound LIB "WINMM.DLL" ALIAS "sndPlaySoundA" (lpszSoundName AS ASCIIZ, BYVAL uFlags AS DWORD) AS LONG
    DECLARE FUNCTION GetTickCount LIB "KERNEL32.DLL" ALIAS "GetTickCount" () AS DWORD
    
    FUNCTION PBMAIN () AS LONG
     LOCAL daOrDit AS STRING, ii AS LONG
      FOR ii = 1 TO 100
      IF RND < .5 THEN daOrDit = "dah.wav" ELSE daOrDit = "dit.wav"
      sndPlaySound BYCOPY daOrDit, 0
      IF RND < .05 THEN millisecDelay(150)
      NEXT
    
    END FUNCTION
    
    FUNCTION millisecDelay(x AS LONG) AS LONG
    
       LOCAL y AS LONG
       y = getTickCount
       DO
          IF getTickCount - y >= x THEN EXIT FUNCTION
       LOOP
    
    END FUNCTION
    Attached Files
    Last edited by John Gleason; 28 Jun 2009, 12:36 PM. Reason: added compiled .exe to zip

    Leave a comment:


  • Michael Mattias
    replied
    A better sounding way would be to create some .WAV files of dits and dahs and silence and string them together to play but that's more complicated to program.
    I like this idea, a lot.

    Maybe even create WAVs for each letter of the alphabet plus combined letters of the common words used in telegrams eg "STOP". The the user might be able to type and have the sounds generated in real time, without learning Morse code at all. (I learned as a Cub Scout. I have forgotten).

    You'd think there must be some Windows functions to "Build WAV or other sound file by specifying a table of frequency and duration" or something like that, wouldn't you?

    [LATER]

    Hmm, thank you, Google(r)....


    1. WinMorse 2.0 (winmorse.com)
    is a FREE text to Morse Code conversion application for Windows. WinMorse converts text into Morse Code, using various Output Formats. Here are some ideas for what you can do with the files created by WinMorse: Use for Morse Code practice/training/testing Accessibility tool for the vision impaired Create Morse Code…
    Type in your message, convert using above to WAV, play. (?)
    Last edited by Michael Mattias; 28 Jun 2009, 10:58 AM. Reason: Add link info

    Leave a comment:


  • Michael Mattias
    replied
    > [SLEEP] makes the thread sleep for that many milliseconds max.

    I believe that should be "that many milliseconds MINIMUM."

    However, the help for SLEEP() doesn't:
    Pause the current thread of the application for a specified number of milliseconds (mSec), allowing other processes (or threads) to continue.
    That Kinda makes it sound like a precision thing, huh?

    >OK so show me how it should really be done.

    If you mean "precision timing" as a general proposition, it ain't SLEEP, or even a waitable timer, or a LOOP querying TIX(). The preemptive multitasking of Windows is downright incompatible with precision timing.

    But if you mean running a background thread with a GUI, I think this is a pretty good starter demo:
    GUI + Worker Thread + Abort Demo 11-24-07


    MCM

    Leave a comment:


  • Rory Davis
    replied
    Thanks Paul, the link you posted was exactly the explanation I needed.

    Leave a comment:


  • Paul Dixon
    replied
    Denys,
    I'm sure there are better ways of sending Morse code but here's one simple method. It assumes you have a system speaker in your computer:
    Code:
    'PBCC5.01 program
    $INCLUDE "WIN32API.INC"
    
    FUNCTION WINMAIN (BYVAL hInstance     AS LONG, _
                      BYVAL hPrevInstance AS LONG, _
                      BYVAL lpCmdLine           AS ASCIIZ PTR, _
                      BYVAL iCmdShow      AS LONG) AS LONG
    
    SendMorse("Hello from Mister Morse")
              
    END FUNCTION
    
    
    SUB SendMorse(m AS STRING)
    LOCAL r AS LONG
    LOCAL symbol, letter, Morse AS STRING
    LOCAL DitDuration, DahDuration AS LONG
    
    DitDuration=50 'milliseconds
    DahDuration = 3 * DitDuration
    
    'a-m
    DATA .-,-...,-.-.,-..,.,..-.,--.,....,..,.---,-.-,.-..,--
    'n-z
    DATA -.,---,.--.,--.-,.-.,...,-,..-,...-,.--,-..-,-.--,--..
    '0-9
    DATA -----,.----,..---,...--,....-,.....,-....,--...,---..,----.
    
    FOR r = 1 TO LEN(m)
        letter = LCASE$(MID$(m,r,1))
        SELECT CASE letter
            CASE "a" TO "z"
                symbol = READ$(ASC(letter)-ASC("a")+1)
            CASE "0" TO "9"
                symbol = READ$(ASC(letter)-ASC("0")+27)
    
            CASE ELSE
                symbol = " "
            END SELECT
    
         Morse = Morse + " " + symbol
         PRINT r,letter,symbol
    
    NEXT
    
    FOR r& = 1 TO LEN(Morse)
        SELECT CASE MID$(Morse,r,1)
            CASE "."
                winbeep 500,DitDuration
                winbeep 20000,DitDuration
            CASE "-"
                winbeep 500,DahDuration
                winbeep 20000,DitDuration
    
            CASE " "
                winbeep 20000,DahDuration
    
        END SELECT
    
    NEXT
    
    END SUB
    A better sounding way would be to create some .WAV files of dits and dahs and silence and string them together to play but that's more complicated to program. However, once strung together, the sound system in the computer will play them at the required speed and timing regardless of what else is running on the computer.

    If you're trying to trigger an outside device then the the multimedia timer demonstrated in the previous post will probably be accurate enough for Morse Code. It's not exact but is probably closer in timing than a human could get.

    Paul.

    Leave a comment:


  • Paul Dixon
    replied
    Rory,
    perhaps this explanation might help:


    If you want quicker response times you can try using multimedia timers which give a resloution of about 1ms:
    Code:
    'PBWin9.01 program
    $INCLUDE "WIN32API.INC"
    DECLARE FUNCTION test( BYVAL uID AS LONG, BYVAL uMsg AS LONG, _
                             BYVAL dwUser AS LONG, BYVAL dw1 AS LONG, BYVAL dw2 AS LONG) AS LONG
    
    GLOBAL msec, CloseFlag AS LONG
    GLOBAL Freq AS QUAD
    GLOBAL Result AS STRING
    
    %UpButton=500
    %DownButton=501
    %CloseButton=502
    %OutputText=503
    %TargetText=504
    
    
    CALLBACK FUNCTION UpButtonClick ()
    IF CB.MSG = %WM_COMMAND AND CB.CTLMSG = %BN_CLICKED THEN
        DECR msec
        FUNCTION = 1
    END IF
     
    END FUNCTION
    
    
    CALLBACK FUNCTION DownButtonClick ()
    IF CB.MSG = %WM_COMMAND AND CB.CTLMSG = %BN_CLICKED THEN
        INCR msec
        FUNCTION = 1
    END IF
    
    END FUNCTION
    
    
    CALLBACK FUNCTION CloseButtonClick ()
    IF CB.MSG = %WM_COMMAND AND CB.CTLMSG = %BN_CLICKED THEN
        CloseFlag=1
        FUNCTION = 1
    END IF
    
    END FUNCTION
    
    
    
    FUNCTION WINMAIN (BYVAL hInstance     AS LONG, _
                      BYVAL hPrevInstance AS LONG, _
                      BYVAL lpCmdLine           AS ASCIIZ PTR, _
                      BYVAL iCmdShow      AS LONG) AS LONG
    
    
    LOCAL count0,count1 AS QUAD
    LOCAL hDlg, TimerHandle AS LONG
    'start the timer
    '1=milliseconds between triggers, 0=maximum timer resolution, test=the routine to call
    TimerHandle = timeSetEvent ( BYVAL 1, BYVAL 0, CODEPTR(test), BYVAL 0&, BYVAL %TIME_PERIODIC)
    
    msec=100' start off aiming for 100msec (10Hz)
    QueryPerformanceFrequency freq
    QueryPerformanceCounter count0
     
                                 
    DIALOG NEW 0,"Test",,,100,100 TO hDlg
    CONTROL ADD BUTTON, hDlg, %UpButton,"Up",10,10,30,20 CALL UpButtonClick
    CONTROL ADD BUTTON, hDlg, %Downbutton,"Down",10,40,30,20 CALL DownButtonClick
    CONTROL ADD BUTTON, hDlg, %CloseButton,"Close",10,70,30,20 CALL CloseButtonClick
    CONTROL ADD LABEL, hDlg, %OutputText,"Freq = ####Hz",50,70,40,20
    CONTROL ADD LABEL, hDlg, %TargetText,"",50,10,40,20
    
    DIALOG SHOW MODELESS hDlg
    
    DO
        DIALOG DOEVENTS
     '   sleep 10
        CONTROL SET TEXT  hDlg, %OutputText, Result
        CONTROL SET TEXT  hDlg, %TargetText, "Target= "+FORMAT$((1000/msec),"###.000")+"Hz"
    LOOP UNTIL CloseFlag
    
    'stop the timer
    timeKillEvent TimerHandle
    DIALOG END hDlg
    END FUNCTION
    
    
    
    FUNCTION test ( BYVAL uID AS LONG, BYVAL uMsg AS LONG, _
                            BYVAL dwUser AS LONG, BYVAL dw1 AS LONG, BYVAL dw2 AS LONG) AS LONG
    'this is the routine that is run everytime the timer triggers
    STATIC COUNT AS LONG
    STATIC count1, count2 AS QUAD
    
          INCR COUNT
          IF COUNT >= msec THEN
              COUNT=0
              QueryPerformanceCounter count2
              Result = "Freq = "+FORMAT$((freq/(count2-count1)),"0000.000")+"Hz"
              count1 = count2
          END IF
          
    END FUNCTION

    Leave a comment:


  • Mike Doty
    replied
    This might be useful:



    Code:
    Sub Sleepy(milliseconds As Long) 'a better sleep
      Dim x As Long, Counter As Long
      Counter = (milliseconds \ 11) + 1  'minimum 1 loop (10 milliseconds)
      For x = 1 To Counter
        DIALOG DoEvents
        Sleep 10
      Next
    End Sub

    Leave a comment:


  • Dave Biggs
    replied
    Anyway, here's the code.... can anyone else replicate?
    Yes.

    I get similar results with my PC (Athlon 64 X2 WinXP SP2).
    I see about 64hz which rises to around 512 whem windows media player is active (playing a DVD etc) with Sleep 1.
    The speed rises to 2000+ if there is no Sleep or Dialog DoEvent in the threaded function.
    I also noted that if I include a msg pump ahead of the MsgBox..
    Code:
        Do Until IsWin(hDlg) = 0
          Dialog DoEvents 
        Loop
    the speed is halved BUT if I drag the Dialog caption bar it doubles again while the mouse button is down (I'd noticed that behavior before in other apps).

    I can't explain any of this except to say that Chris is right in that you can't really predict how many time slices the OS will give your application. From the above it looks as if ActiveX muscles in on the act and also that the OS allows more time slices to your app when it knows a drag operation is going on. All of which makes running time critical tasks a bit awkward .

    Leave a comment:


  • Conrad Hoffman
    replied
    My density is pretty high regarding multiple threads as well, and I haven't had much need for 'em yet. I do know that you can't have all the processor time, and you can't predict just how much you can have. Your loop runs about 4khz on my Athlon64 using SLEEP 0. You can also use a DIALOG DOEVENTS 0 instead, with about the same results. I've run some very fast loops by not updating GUI displays on every pass. You could update every hundred passes and probably gain a lot.

    Leave a comment:


  • Rory Davis
    replied
    I guess I should just give up and admit I'm too stupid to do this. All my apps have to be single-threaded cause I'm just too thick to use threading properly.

    The point was not to try to control the loop frequency with the sleep, the sleep was just to keep the loop from hogging processor time. That the loop frequency fell close to what the sleep period would create was incidental. I KNOW BETTER THAN TO USE SLEEP IN TIMING LOOPS DAGNABBIT! However I AM concerned with how fast I can get a loop to run because time constraints talking to the outside world.

    The point I am trying to understand is what is causing the loop to bog down when there are only "hidden" processes and why does it suddenly stop bogging when other processes are running.

    OK so show me how it should really be done.
    Last edited by Rory Davis; 27 Jun 2009, 11:06 PM.

    Leave a comment:


  • Denys Brosnan
    replied
    Sorry Rory. Chris is right. I have a program which sends morse code which requires correct timing to get the correct length of each "dit" and "dah" else the code ends up all jumbled. Using DOS as a platform it is easy to keep correct timing but with Windows I have to use a seperate piece of equipment to make the morse code readable. Don't worry, I spent a lot of time before admitting defeat.

    Leave a comment:


  • Chris Boss
    replied
    SLEEP is not some kind of timing control function. You can't use SLEEP with a specific value and expect a consistant timing of the thread.

    All sleep does is relinquish the current threads time slice to the next thread which Windows chooses to be next. A value of zero relinquishes whats left of the current threads time slice and a non-zero value makes the thread sleep for that many milliseconds max.

    What you don't see is how other threads are being executed by Windows.

    Especially with Windows XP and later, there may be dozens of processes and threads running, even if you are not running a single program. Just take a look at how many services the PC is running at the time and you will see how much Windows is doing. Depending upon the software installed, the hardware, drivers, etc. each PC is uniquely different in how many processes or threads may be running at any given time.

    To make things even more complex, processes and threads have different priorities. For example some hardware drivers may have a very high priority while other threads/processes have a lower priority. Windows is determining how much time each process or thread is given for its time slice and then it continuously moves from thread/process to thread/process. It never stops.

    A clue to all of this is in the description of Windows as a "multi-tasking" operating, meaning it is doing many, many different things seemly all at the same time (actually it is only doing one thing at a time, but keeps switching threads so fast, they appear to be running simultaneously.

    Do not think you can somehow get exact control of all of this with the Sleep command. You have no control of all of this timing. You can change a threads priority to get more time. You can make a thread sleep to relinquish some of its time. Yet you can't control the exact amount of time being used, since Windows has all control of this and it depends upon how many processes/threads are running at any given time, what each process/thread is doing and their priority settings.

    Now add to this how much memory the PC has, which can also affect this, since when time slices are switched (a context switch) Windows has to do some fancy footwork to make each process think it is all alone and has its own memory blocks (including the settings on the CPU registers). The problem is that what makes all of this work is that a certain amount of memory is needed and if the memory is below that threshhold, Windows must do page swapping to the harddrive. So depending upon how much page swapping is going on, things can either be slowed down or speed up.

    Simply put, there are too many factors involved here to think you can somehow control exact timing with the little old SLEEP command.

    To make things worse, your code is calling a GUI command within the worker thread.

    ie.

    Code:
       DO
            SLEEP 2   'compare with sleep 1, sleep 0, dialog doevents
            DIALOG SET TEXT x, display_loopfreq()
        LOOP UNTIL gThreadrun = 0
    You may not consider this significant, but it is oh so much!

    DIALOG SET TEXT calls API's which will force Windows to so a context switch (switch threads) and jump from the worker thread to the primary GUI thread. This context switch adds a lot of over head and slows things down. You are simply just adding one more problem to the mix. You call SLEEP (which could possibly switch to the next thread which may be the primary thread) and immediately afterwards force a context switch again by calling DIALOG SET TEXT.

    One must fully understand how threads really work before using them and also the ramifications of calls to things like SLEEP or GUI commands.

    It is easy to create and use threads, but it is much more difficult to calculate the effects of the code you put into that thread and how it effects timing.
    Last edited by Chris Boss; 27 Jun 2009, 09:57 PM.

    Leave a comment:


  • Rory Davis
    started a topic Thread loop speed with Sleep

    Thread loop speed with Sleep

    I ran into a phenomenon that I don't understand.

    Basically it goes like this. If I run this code, it displays the frequency the loop cycles. It is a very high frequency if the SLEEP is 0 or is omitted. If it is SLEEP 1 (ms), it should be nominal 1000Hz while SLEEP 2 it's 500Hz.

    However, the frequency varies from machine to machine. On my vista laptop with dual core, it runs at 64Hz nominal until I run Firefox on a page that has graphics - it then runs at the predicted frequency. Closing Firefox causes it to return to 64Hz. MSIE however doesn't have the same effect, nor does opening Firefox to a local jpg file. I also found if I open and run my DVD player or other prog that uses DirectX video, the speed goes to where it should.

    On my wife's XP laptop it runs at 171 Hz regardless of other apps running. On another machine with no other progs running it shows predicted speed.

    Any ideas? Virus infection? Spyware?

    Anyway, here's the code.... can anyone else replicate?

    Code:
    #COMPILE EXE
    #DIM ALL
    #IF NOT %DEF(%WINAPI)
        #INCLUDE "WIN32API.INC"
    #ENDIF
    
    GLOBAL qFreq AS QUAD
    GLOBAL qStartCount AS QUAD
    GLOBAL qEndCount AS QUAD
    
    FUNCTION InitHiresTimer() AS DWORD
        CALL QueryPerformanceFrequency(qFreq)
    END FUNCTION
    
    MACRO startHiresTimer
        CALL QueryPerformanceCounter(qStartCount)
    END MACRO
    
    FUNCTION display_loopfreq() AS STRING
        QueryPerformanceCounter(qEndCount)
        FUNCTION =  FORMAT$(qFreq/(qEndCount-QStartCount),"#.0")+" Hz"
        QueryPerformanceCounter(qStartCount)
    END FUNCTION
    
    'THREAD FUNCTION loopthread(BYVAL x AS DWORD) AS DWORD 'pbwin9
    FUNCTION loopthread(BYVAL x AS DWORD) AS DWORD
        initHirestimer()
        startHiresTimer()
        DO
            SLEEP 2   'compare with sleep 1, sleep 0, dialog doevents
            DIALOG SET TEXT x, display_loopfreq()
        LOOP UNTIL gThreadrun = 0
    END FUNCTION
    
    FUNCTION PBMAIN () AS LONG
        LOCAL x AS DWORD
        LOCAL hdlg AS DWORD
        LOCAL hThread AS DWORD
        DIALOG NEW %HWND_DESKTOP, "", 0,0, 100,0 TO hdlg
        DIALOG SHOW MODELESS hDlg
        GLOBAL gThreadrun AS DWORD
        gThreadrun = 1
        THREAD CREATE loopthread(hdlg) TO hThread
        THREAD CLOSE hThread TO hThread
    
        MSGBOX "Quit?",,"Loopspeed" 'This holds loop running until msgbox closes
        gThreadrun = 0
    END FUNCTION
Working...
X