Announcement

Collapse
No announcement yet.

Thread Question - Understanding Use of Sleep

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

  • Thread Question - Understanding Use of Sleep

    I'm playing with threads today, so I'm sure this is only the first of several questions I will ask.

    I tried the simple code below, expecting both threads to run simultaneously. On my PC, the main thread executes almost immediately, then the second thread starts and runs really slowly (by comparison to the main thread). Regardless of the values I use in sleep (0, 1, 5, 50) , I get the same results - thread NewThread executes sequentially after MainThread.

    From this Help comment on SLEEP, I expected NewThread to run in parallel with the main thread.

    Pause the current thread of the application for a specified number of milliseconds (mSec), allowing other processes (or threads) to continue.
    Can someone clarify for me what I'm misunderstanding?

    Code:
    'Compilable Example:
    #Compile Exe
    #Dim All
    #Include "Win32API.inc"
    Global hDlg As Dword, hThread As Dword
    Function PBMain() As Long
       Dialog New Pixels, 0, "Test Code",300,300,200,200, %WS_OverlappedWindow To hDlg
       Control Add Button, hDlg, 100,"Start Thread", 50,10,100,20
       Control Add Label, hDlg, 200,"<main thread count>", 50,40,100,20
       Control Add Label, hDlg, 300,"<extra thread count>", 50,70,100,20
       Dialog Show Modal hDlg Call DlgProc
    End Function
    
    CallBack Function DlgProc() As Long
       If Cb.Msg = %WM_Command And Cb.Ctl = 100 And Cb.CtlMsg = %BN_Clicked Then
         Thread Create NewThread(0) To hThread       'start the new thread, argument not used
          Thread Close hThread To hThread    'suggested by PowerBASIC Inc. as good practice
          MainThread    'in the main thread, keep doing something
       End If
    End Function
    
    Function MainThread () As Long
         Local iCount&
         Do
            Incr iCount& : Control Set Text hDlg, 200, "Main" + Str$(iCount&)
            Sleep 0
         Loop Until iCount& > 2000
    End Function
    
    Thread Function NewThread (ByVal x As Long) As Long
         Local iCount&
         Do
            Incr iCount& : Control Set Text hDlg, 300, "Thread" + Str$(iCount&)
            Sleep 0
         Loop Until iCount& > 2000
    End Function

  • #2
    > the main thread executes almost immediately, then the second thread starts and runs really slowly (by comparison to the main thread)

    In fact, the second thread does not start until the main thread has completed. Try 20000 instead of 2000.

    After the main thread has completed left click and hold the title bar and watch the second thread run flat out. Release the button and the second thread slows down again.

    If we put 'Dialog DoEevents 0' in MainThreads loop then MainThread still starts before NewThread but both functions now run flat out.

    I did think I knew the reason for this behaviour but I have since changed my mind and am now scratching my head again.
    Last edited by David Roberts; 9 Oct 2009, 04:22 PM.

    Comment


    • #3
      >I'm playing with threads today,

      ooh, ooh, one of my favorite topics.

      >Control Set Text hDlg, 200, "Main" + Str$(iCount&)

      "Assuming" CONTROL SET TEXT uses SetWindowText, a thread switch is forced when executed, becase SetWindowText (window owned by calling process) uses WM_SETTEXT message, and the window procedure always executes in the context of the window-owning thread.

      So you are constantly switching threads.

      In addition, the thread context in which the active window executes gets preferential treatment when it comes to getting CPU time.

      The bottom line is, "empty loops" are no way to see what's going on in a multi-threaded program.


      If you are looking at doing a "GUI with a background task in a separate thread of execution" you will want to look at and study this demo:
      GUI + Worker Thread + Abort Demo 11-24-07

      I have a bunch of other multi-threaded demos in the source code forum. However, most do not have "thread" in the title since I used additional threads because it seemed like the right thing to do, not because "THREAD CREATE" was particularly fascinating.

      MCM
      Michael Mattias
      Tal Systems (retired)
      Port Washington WI USA
      [email protected]
      http://www.talsystems.com

      Comment


      • #4
        > ooh, ooh, one of my favorite topics.

        I knew that and was hoping you'd come in.

        However, the fact that the secondary thread does not start, even though it exists according to Task Manager, until the function MainThread has completed still remains unanswered.

        Comment


        • #5
          'Dialog DoEevnts 0' in MainThread doesn't help if MainThread is executed before Thread Create - NewThread doesn't get a look in until MainThread completes in this case either.

          Comment


          • #6
            It still hasn't fully sunk in yet but 'Control Set Text ...' is the problem.

            If we replace
            Incr iCount& : Control Set Text hDlg, 200, "Main" + Str$(iCount&)
            with simply
            Incr iCount&
            then NewThread executes and without using 'Dialog DoEvents 0' but its execution is pitifully slow.

            On the other hand if we dump MainThread and use two secondary threads then we see a noticeable improvement in speed and it looks a looks a lot simpler as well.
            Code:
            #Compile Exe
            #Dim All
            #Include "Win32API.inc"
            
            Global hDlg As Dword, hThread As Dword
            
            Function PBMain() As Long
              Dialog New Pixels, 0, "Test Code",300,300,200,200, %WS_OverlappedWindow To hDlg
              Control Add Button, hDlg, 100,"Start Thread", 50,10,100,20
              Control Add Label, hDlg, 200,"<main thread count>", 50,40,100,20
              Control Add Label, hDlg, 300,"<extra thread count>", 50,70,100,20
              Dialog Show Modal hDlg Call DlgProc
            End Function
            
            CallBack Function DlgProc() As Long
              If CB.Msg = %WM_Command And CB.Ctl = 100 And CB.CtlMsg = %BN_Clicked Then
                Thread Create NewThread(200) To hThread       'start the new thread, argument not used
                Thread Close hThread To hThread    'suggested by PowerBASIC Inc. as good practice
                Thread Create NewThread(300) To hThread       'start the new thread, argument not used
                Thread Close hThread To hThread    'suggested by PowerBASIC Inc. as good practice
              End If
            End Function
            
            Thread Function NewThread (ByVal x As Long) As Long
              Local iCount&
              Do
                Incr iCount& : Control Set Text hDlg, x, "Thread" + Str$(iCount&)
              Loop Until iCount& > 200000
            End Function

            Comment


            • #7
              >the fact that the secondary thread does not start

              That thread exists as soon as THREAD CREATE returns, and is competing for CPU time just like every other thread. That is, it HAS started, it just hasn't had enough time for it to do whatever it does yet. That THREAD CREATE returned a handle tells you it has 'started.'


              You could also try posting (not sending) a WM_SETTEXT message instead of using CONTROL SET TEXT.
              Michael Mattias
              Tal Systems (retired)
              Port Washington WI USA
              [email protected]
              http://www.talsystems.com

              Comment


              • #8
                > That thread exists as soon as THREAD CREATE returns

                I know.

                > the fact that the secondary thread does not start, even though it exists according to Task Manager

                As you probably know 'THREAD CREATE' is asynchronous so even when we get back to the parent thread initialization is still undergoing. I have tried slowing things down a bit before calling MainThread but NewThread will still not start without the 'Dialog DoEvents 0'.

                > So you are constantly switching threads.

                I thought about that and figured that was why NewThread was slow when 'Incr iCount&' was on its own. However, with the two secondary threads approach we get our speed back.

                > You could also try posting (not sending) a WM_SETTEXT message instead of using CONTROL SET TEXT.

                Will do.

                Comment


                • #9
                  >'THREAD CREATE' is asynchronous

                  Um, well, only 'kinda.' The execution of the THREAD CREATE statement is atomic... your code in the calling thread does not continue until the thread object has been created or the creation has failed.

                  The thread FUNCTION executes asynchronously, and unless you get real cute, only when Windows' thinks it is time for it to do so.

                  MCM
                  Michael Mattias
                  Tal Systems (retired)
                  Port Washington WI USA
                  [email protected]
                  http://www.talsystems.com

                  Comment


                  • #10
                    BTW....
                    Code:
                    Thread Close hThread To hThread    [COLOR="Red"]'suggested by PowerBASIC Inc. as good practice[/COLOR]
                    Closing handles when no longer needed is good practice for any handle: file handles, bitmap handles, process handles, thread handles, widget handles.

                    There is absolutely nothing wrong with retaining a thread handle in an open state if you are going to need it... as in ...

                    Code:
                    FUNCTION PBMAIN() 
                    
                    '
                        THREAD CREATE   somefunction  TO  [COLOR="Red"]hThread [/COLOR]   ' start worker thead 
                    ' create screen 
                              DIALOG NEW 
                                CONTROL ADD 
                                CONTROL ADD 
                    
                    ' Don't show the screen until worker thread function has completed:
                         WaitForSingleObject [COLOR="Red"] hThread[/COLOR], %INFINITE
                    ' We don't get here until the worker thread function has completed
                    ' NOW we are done with the thread handle
                        THREAD CLOSE hThread TO what_should_have_been_optional_and_set_ERR_instead
                    ' with worker thread function complete, show the user screen....
                         DIALOG SHOW MODAL....
                    
                    
                    END FUNCTION
                    In above, the thread function can be some kind of initialization function, or maybe a splash screen you want up while you do some other startup tasks. (code to terminate a splash screen or any other GUI thread not shown here, but there was a BBS thread here last week on this subject).

                    MCM
                    Last edited by Michael Mattias; 10 Oct 2009, 10:00 AM. Reason: Enhanced example
                    Michael Mattias
                    Tal Systems (retired)
                    Port Washington WI USA
                    [email protected]
                    http://www.talsystems.com

                    Comment


                    • #11
                      Actually, I'm not that au fait with messages.

                      I couldn't get PostMessage to work. SendMessage did but only if I used 'Dialog DoEvents 0'.

                      I must be doing something wrong with PostMessage.

                      What is wrong with this? SendMessage works OK.

                      Code:
                      #Compile Exe
                      #Dim All
                      #Include "Win32API.inc"
                      
                      %Button = 100
                      %Message = 110
                                    
                      Function PBMain() As Long
                        Local hDlg As Long
                        Dialog New  0, "Message test", , , 100, 100, %WS_SYSMENU, , To hDlg
                        Control Add Button, hDlg, %Button,"Message", 25,10,50,20
                        Control Add Label, hDlg, %Message,"<Message here>", 24, 40, 70, 40
                        Dialog Show Modal hDlg Call DlgProc
                      End Function
                      
                      CallBack Function DlgProc() As Long
                        Local messText As String
                        Local hMessage As Dword
                        
                        Control Handle CB.Hndl, %Message To hMessage
                        
                        Select Case As Long CB.Msg
                        
                          Case %WM_COMMAND
                            
                            Select Case As Long CB.Ctl
                              
                              Case %Button
                                If CB.CtlMsg = %BN_CLICKED Then
                                  messText = "That worked"
                                  PostMessage hMessage, %WM_SETTEXT, 0 , ByVal StrPtr(messText)
                                End If
                                    
                            End Select ' WM_COMMAND
                                  
                        End Select ' CBMsg
                      
                      End Function
                      Last edited by David Roberts; 10 Oct 2009, 12:12 PM.

                      Comment


                      • #12
                        >PostMessage hMessage, %WM_SETTEXT, 0 , ByVal StrPtr(messText)

                        For one thing, you don't need to avoid CONTROL SET TEXT when you are responding to a notification message when executing in the context of your primary (in your case, only) thread of execution.

                        Second...
                        >Local messText As String
                        > ...
                        > ....ByVal StrPtr(messText)

                        When your Window procedure is entered to process the WM_SETTEXT message, the LOCAL variable 'messText' is out of scope and STRPTR(messtext) is an invalid pointer.

                        This would be a good example to look at the TRACE.

                        You'll see you "enter procedure" to process WM_COMMAND, then "Exit procedure", then "enter procedure" a second* time to process WM_SETTEXT.

                        * actually many more times than two, but for our purposes only these two notifcations matter.
                        Michael Mattias
                        Tal Systems (retired)
                        Port Washington WI USA
                        [email protected]
                        http://www.talsystems.com

                        Comment


                        • #13
                          This uses 2 secondary threads. .... edited at 1:42 to fix error...

                          With Control Set Text, both controls are updated.
                          With PostMessage, the controls are NOT updated.
                          With SendMessage, the controls are updated.

                          Shouldn't all 3 work?

                          Code:
                          'Compilable Example:
                          #COMPILE EXE
                          #DIM ALL
                          #INCLUDE "Win32API.inc"
                          GLOBAL hDlg AS DWORD
                          FUNCTION PBMAIN() AS LONG
                             DIALOG NEW PIXELS, 0, "Test Code",300,300,200,200, %WS_OVERLAPPEDWINDOW TO hDlg
                             CONTROL ADD BUTTON, hDlg, 100,"Start Thread", 50,10,100,20
                             CONTROL ADD LABEL, hDlg, 200,"<1st thread count>", 50,40,100,20
                             CONTROL ADD LABEL, hDlg, 300,"<2nd thread count>", 50,70,100,20
                             DIALOG SHOW MODAL hDlg CALL DlgProc
                          END FUNCTION
                          
                          CALLBACK FUNCTION DlgProc() AS LONG
                             IF CB.MSG = %WM_COMMAND AND CB.CTL = 100 AND CB.CTLMSG = %BN_CLICKED THEN
                                LOCAL hThread AS DWORD
                                THREAD CREATE NewThread(200) TO hThread       'start the new thread, argument is control ID
                                THREAD CLOSE hThread TO hThread 
                                THREAD CREATE NewThread(300) TO hThread       'start the new thread, argument is control ID
                                THREAD CLOSE hThread TO hThread 
                             END IF
                          END FUNCTION
                          
                          Thread Function NewThread (ByVal x As Long) As Long
                               Local iCount&, hControl as Dword, tempZ as AsciiZ * 200
                               Control Handle hDlg, x To hControl
                               Do
                                  Incr iCount&
                                   tempZ = "Thread" + Str$(iCount&)        
                          '        Control Set Text hDlg, x, tempZ
                                  PostMessage hControl, %WM_SETTEXT, 0 , ByVal VarPTR(tempz)
                          '        SendMessage hControl, %WM_SETTEXT, 0 , ByVal VarPTR(tempz)
                               Loop Until iCount& > 2000
                          End Function
                          Last edited by Gary Beene; 10 Oct 2009, 01:42 PM.

                          Comment


                          • #14
                            >For one thing, you don't need to avoid CONTROL SET TEXT ...

                            I understand that, I wanted to see PostMessage work.

                            > When your Window procedure is entered to process the WM_SETTEXT message, the LOCAL variable 'messText' is out of scope and STRPTR(messtext) is an invalid pointer.

                            Ouch!

                            OK, I'll post a private message with a value in CB.wParam

                            Code:
                            #Compile Exe
                            #Dim All
                            #Include "Win32API.inc"
                            
                            %Button = 100
                            %Message = 110
                            %Private = %WM_USER + 501
                                          
                            Function PBMain() As Long
                              Local hDlg As Long
                              Dialog New  0, "Message test", , , 100, 100, %WS_SYSMENU, , To hDlg
                              Control Add Button, hDlg, %Button,"Message", 25,10,50,20
                              Control Add Label, hDlg, %Message,"<Message here>", 24, 40, 70, 40
                              Dialog Show Modal hDlg Call DlgProc
                            End Function
                            
                            CallBack Function DlgProc() As Long
                              Local messText As String
                              Local hMessage As Dword
                              Local iCount As Long
                              
                              Select Case As Long CB.Msg
                              
                                Case %Private
                                  Control Set Text CB.Hndl, %Message, "Text" + Str$(CB.wParam) 
                                    
                                Case %WM_COMMAND
                                  
                                  Select Case As Long CB.Ctl
                                    
                                    Case %Button
                                      If CB.CtlMsg = %BN_CLICKED Then
                                        iCount = 12345
                                        PostMessage CB.Hndl, %Private, iCount, 0
                                      End If
                                          
                                  End Select ' WM_COMMAND
                                        
                              End Select ' CBMsg
                            
                            End Function
                            That works. Now I'll get back to Gary's problem code.
                            Last edited by David Roberts; 10 Oct 2009, 05:03 PM.

                            Comment


                            • #15
                              A good explanation of the difference between postmessage and sendmessage here in msdn magazine

                              Comment


                              • #16
                                Gary, have a look at the private message for posting in my last post - seems to do the trick.

                                PostMessage is not working as hoped in Gary's original post. I still need 'Dialog DoEvents 0' else NewThread will not start until MainThread has completed. This was the case with SendMessage as well but PostMessage sees the updates much faster; not surprisingly since we aren't waiting.

                                Here is is where I am now - slight change as I write expecting to add more.

                                Code:
                                #Compile Exe
                                #Dim All
                                #Include "Win32API.inc"
                                
                                Global hDlg As Dword, hThread As Dword
                                Global PackUp As Long
                                
                                %Start = 100
                                %Main = 110
                                %Extra = 120
                                %Private = %WM_USER + 501
                                
                                Function PBMain() As Long
                                  Dialog New Pixels, 0, "Test Code",300,300,200,200, %WS_OverlappedWindow To hDlg
                                  Control Add Button, hDlg, %Start,"Start Thread", 50,10,100,20
                                  Control Add Label, hDlg, %Main,"<main thread count>", 50,40,100,20
                                  Control Add Label, hDlg, %Extra,"<extra thread count>", 50,70,100,20
                                  Dialog Show Modal hDlg Call DlgProc
                                End Function
                                
                                CallBack Function DlgProc() As Long
                                
                                  Select Case As Long CB.Msg
                                  
                                    Case %Private
                                      Control Set Text CB.Hndl, %Extra, "Thread" + Str$(CB.wParam)
                                      
                                    Case %WM_COMMAND
                                      
                                      Select Case As Long CB.Ctl
                                        
                                        Case %Start
                                          If CB.CtlMsg = %BN_CLICKED Then
                                            Control Disable hDlg, %Start
                                            Thread Create NewThread(0) To hThread
                                            Thread Close hThread To hThread
                                            MainThread
                                            Control Enable hDlg, %Start
                                          End If
                                              
                                      End Select ' WM_COMMAND
                                      
                                  End Select ' CB.Msg  
                                                     
                                End Function
                                
                                Function MainThread () As Long
                                  Local iCount&
                                  Do
                                    Incr iCount& : Control Set Text hDlg, %Main, "Main" + Str$(iCount&)
                                    Dialog DoEvents 0 ' <--- Comment out and see NewThread wait!
                                  Loop Until iCount& > 20000
                                End Function
                                
                                Thread Function NewThread (ByVal x As Long) As Long
                                  Local iCount&
                                  Do
                                    Incr iCount : PostMessage hDlg, %Private, iCount&, 0
                                  Loop Until iCount& > 20000
                                End Function
                                Last edited by David Roberts; 10 Oct 2009, 05:02 PM.

                                Comment


                                • #17
                                  Thanks Chris, I have just bookmarked that.

                                  Comment


                                  • #18
                                    Originally posted by David Roberts View Post
                                    Gary, have a look at the private message for posting in my last post - seems to do the trick...
                                    Only because the value you use happens not to be used as a windows message. %WM_USER + n where n > 500 has advantages.

                                    Comment


                                    • #19
                                      Chris, I was messing with something else and a button's text went haywire. This put me right and I edited the above 14 minutes before you mentioned it.

                                      Comment


                                      • #20
                                        No, it didn't put me right - you did.

                                        I've just had another problem - changed both above to %WM_USER + 501.

                                        Comment

                                        Working...
                                        X