Announcement

Collapse
No announcement yet.

Thread Question - Understanding Use of Sleep

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

  • David Roberts
    replied
    I wholeheartedly agree, Chris.

    I prefer to keep the handle open just in case I need to use it. As I said above "I write expecting to add more" - just look at those 'Select Case' statements - I can easily add, perhaps for debugging purposes; in fact I did during testing.

    I don't forget to close the handle because in my own code I use
    Code:
    CASE %WM_DESTROY
      IF hThread THEN THREAD CLOSE hThread TO lResult
    The exercise for me here was to solve a problem before I had no hair left and not 'clean up' Gary's code. I am not suggesting Gary's code needed cleaning up although I don't like 'If CB.Msg = %WM_Command And CB.Ctl = 100 And CB.CtlMsg = %BN_Clicked' especially that '100'.

    Dirty code will find itself being introduced as we strife to solve something; with most folks I reckon. However, on solving we should then clean up. When I'm 'on the attack' I don't write with newcomers in mind either. When I post in the Source Code forum I still don't write with newcomers in mind but I do strive to publish good clean code there. Having said that I would probably look at some of my early entries and say "Oh, dear" but that is probably true for anyone who contributes to the Source Code forum from time to time.

    Perhaps we should have a 'sticky' for newcomers: You will find some good programming practice in all of the forums but beware that in forums other than the Source Code forum when the 'blood is up' dirty code may creep in. Regrettably we could grow potatoes on some of the code in the Source Code forum but the likelihood is less than elsewhere.
    Last edited by David Roberts; 11 Oct 2009, 05:26 AM.

    Leave a comment:


  • Chris Holbrook
    replied
    Originally posted by David Roberts View Post
    You actually program when Coronation Street is on?
    I do not use a television, although I do own a postcard signed by Julie Goodyear (Bet Lynch).

    My aim is to strive to improve my Windows programming skills which were in a fairly primal state in the year in question. This is a higher goal for me than consistency! I can only apologise for past misdemeanours.

    Embracing prolixity with the benefit of newcomers to programming as my guiding light, I will point out the general danger of having a function sacrifice its handle to a return value like a snake swallowing its own tail (Kekulé's inspiration?). If the function fails, then you are left with a returned value indicating failure, but no handle to the failed object, and therefore no alternative than to soldier on with a failed object lurking around using resources, or to kill the parent process. Unless you enumerate the threads and have enough evidence to pick the right one. In the specific I don't know how recoverable a failed THREAD CLOSE generally is.

    Anyway as a programming technique it is best avoided. Some of us learnt our programming skills in an environment which encourages recycling of variables, but one can go too far.

    Ooo, I've just had an MCM moment! Must be old age.

    Leave a comment:


  • David Roberts
    replied
    Gary used it in his opening post and I left it as is. Do a POFFS 'Find exact match' search on 'hthread to hthread'. You may be surprised at who uses it. You actually participated in two threads in June 2006 which uses it and one was a Source code entry by yourself on Wednesday June 14 2006 at 19:38. You actually program when Coronation Street is on?

    Leave a comment:


  • Chris Holbrook
    replied
    Originally posted by David Roberts View Post
    Code:
                Thread Close hThread To hThread
    pardon?

    Leave a comment:


  • David Roberts
    replied
    Cracked it. :coffee2:

    The 'Control Set Text' in MainThread was also causing a problem.

    So, I used PostMessage there as well.

    If we change 'Post' to 'Send' in MainThread then we get NewThread coming in after MainThread.

    If we change 'Post' to 'Send' in NewThread then we get a pedestrian NewThread.

    However, both functions now spit out iCount& so fast that I have had to restrict the reporting to iCount& increments of 50 for my machine. I'd be interested in knowing if that is OK for other machines.

    So, no 'Dialog DoEvents 0' now and we are using a primary thread plus secondary thread as Gary's original post.

    I've done a bit of tidying up as well and if I may coin a phrase from you know who "Look, Ma, no globals".
    Code:
    #Compile Exe
    #Dim All
    #Include "Win32API.inc"
    
    %Start = 100
    %Main = 101
    %Extra = 102
    %Private = %WM_USER + 501
    
    Function PBMain() As Long
      Local hDlg 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
      Local hThread As Long
      
      Dim Text( 0 To 1 ) As String
      Text(0) = "Main"
      Text(1) = "Thread"
      
      Select Case As Long CB.Msg
      
        Case %Private
          Control Set Text CB.Hndl, %Main + CB.lParam, Text(CB.lParam) + Str$(CB.wParam)
          
        Case %WM_COMMAND
          
          Select Case As Long CB.Ctl
            
            Case %Start
              If CB.CtlMsg = %BN_CLICKED Then
                Thread Create NewThread(CB.Hndl) To hThread
                Thread Close hThread To hThread
                MainThread(CB.Hndl)
              End If
                  
          End Select ' WM_COMMAND
          
      End Select ' CB.Msg  
                         
    End Function
    
    Function MainThread (ByVal hWnd As Long) As Long
      Local iCount&
      Do
        Incr iCount&
        If iCount& Mod 50 = 0 Then PostMessage hWnd, %Private, iCount&, 0
      Loop Until iCount& > 200000
    End Function
    
    Thread Function NewThread (ByVal hWnd As Long) As Long
      Local iCount&
      Do
        Incr iCount&
        If iCount& Mod 50 = 0 Then PostMessage hWnd, %Private, iCount&, 1
      Loop Until iCount& > 200000
    End Function
    Last edited by David Roberts; 10 Oct 2009, 08:21 PM.

    Leave a comment:


  • David Roberts
    replied
    No, it didn't put me right - you did.

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

    Leave a comment:


  • David Roberts
    replied
    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.

    Leave a comment:


  • Chris Holbrook
    replied
    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.

    Leave a comment:


  • David Roberts
    replied
    Thanks Chris, I have just bookmarked that.

    Leave a comment:


  • David Roberts
    replied
    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.

    Leave a comment:


  • Chris Holbrook
    replied
    A good explanation of the difference between postmessage and sendmessage here in msdn magazine

    Leave a comment:


  • David Roberts
    replied
    >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.

    Leave a comment:


  • Gary Beene
    replied
    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.

    Leave a comment:


  • Michael Mattias
    replied
    >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.

    Leave a comment:


  • David Roberts
    replied
    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.

    Leave a comment:


  • Michael Mattias
    replied
    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

    Leave a comment:


  • Michael Mattias
    replied
    >'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

    Leave a comment:


  • David Roberts
    replied
    > 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.

    Leave a comment:


  • Michael Mattias
    replied
    >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.

    Leave a comment:


  • David Roberts
    replied
    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

    Leave a comment:

Working...
X