Button, thread

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts
  • Chris Holbrook
    Member
    • Aug 2005
    • 7118

    Button, thread

    I have very little experience of threads, just what I've read in the help and read here. I don't have any conceptual problems with concurrent stuff, just never dabbled with it in Windows or PB.

    The application runs a long running process which can be interrupted by setting a flag. In my first attempt, the thread which sets the flag puts up a MSGBOX, sets the flag and clears the msgbox when the MSGBOX button is clicked. This works, but if the button is NOT clicked, how to get rid of the MSGBOX? CLOSE THREAD doesn't do it.

    Is there a way in which the new thread can get messages from a button in the dialog created by the main process, rather than starting a new (MSGBOX) window? This would look better.
    Last edited by Chris Holbrook; 27 May 2008, 06:20 AM.
  • Michael Mattias
    Member
    • Aug 1998
    • 43447

    #2
    Thread objects do not receive messages; windows receive messages. Threads do not set flags; procedures set flags.

    However, these demos may be of use to you.....

    Terminate Worker Threads Using Windows Events (and Using Thread Local Storage) Demo Dec 23 2005

    And/or

    Progress Bar Dialog for PB/CC programs October 24, 2002
    (Ignore the "PB/CC" in the thread title. This will work in a GUI program exactly the same way).
    Michael Mattias
    Tal Systems (retired)
    Port Washington WI USA
    [email protected]
    http://www.talsystems.com

    Comment

    • Chris Holbrook
      Member
      • Aug 2005
      • 7118

      #3
      MCM, thanks for your reply, it lead me indirectly to this thread and Chris Boss's suggestion, which so far works 100% in my test context, no need for threads at all, just put a DIALOG DOEVENTS in the slow running part.

      Unfortunately, it would not work if the slow running bit was an external call - as mine is in the "real world" so it's back to threads again!

      Comment

      • Chris Holbrook
        Member
        • Aug 2005
        • 7118

        #4
        DIALOG DOEVENTS eg

        Using Dialog DoEvents to allow message processing so that a control can be used to interrupt a long running process:

        Code:
        #COMPILE EXE
        #DIM ALL
        #INCLUDE "WIN32API.INC"
        #INCLUDE "COMMCTRL.INC"
        %IDD_DIALOG1             =  101
        %IDC_BUTTON1             = 1001
        %IDC_BUTTON2             = 1002 '*
        %IDC_TEXTBOX1            = 1003 '*
        %IDC_RESULTS_LB          = 1004 '*
        %IDC_STATS_LAB           = 1005 '*
        %IDC_PROG1 = 1006
        
        GLOBAL gflag AS LONG
        '-------------------------------------------------------------------------------
        SUB long_runner ( hD AS DWORD, t AS DOUBLE)
            LOCAL i, j, k AS LONG
            LOCAL n, untilnow, tstart AS DOUBLE
            
            tstart = TIMER
            WHILE untilnow < t
                CONTROL SEND hD, %IDC_PROG1, %PBM_SETPOS, untilnow*100/t, 0
                IF gflag = 1 THEN
                    ? "interrupted!"
                    EXIT SUB
                END IF
                INCR n
                untilnow = TIMER - tstart
                DIALOG DOEVENTS 1
                SLEEP 50
            WEND
            ? "finished long runner"
        END SUB
        '-------------------------------------------------------------------------------
        CALLBACK FUNCTION ShowDIALOG1Proc()
        
            LOCAL lresult, n AS LONG
           
        
            SELECT CASE AS LONG CBMSG
                CASE %WM_INITDIALOG
                CASE %WM_COMMAND
                    SELECT CASE AS LONG CBCTL
                        CASE %IDC_BUTTON1
                            IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                                long_runner(CBHNDL, 20)
                                gflag = 0
                            END IF
                        CASE %IDC_BUTTON2
                            gflag = 1
                    END SELECT
                CASE %WM_DESTROY
            END SELECT
        END FUNCTION
        '-----------------------------------------------------------------------
        FUNCTION ShowDIALOG1(BYVAL hParent AS DWORD) AS LONG
            LOCAL lRslt AS LONG
            LOCAL hDlg  AS DWORD
        
            DIALOG NEW hParent, "Interrupt a long-running process", 175, 118, 170, 58, %WS_POPUP OR %WS_BORDER OR %WS_DLGFRAME OR _
                %WS_SYSMENU OR %WS_CLIPSIBLINGS OR %WS_VISIBLE OR %DS_MODALFRAME OR %DS_3DLOOK OR %DS_NOFAILCREATE OR %DS_SETFONT, _
                %WS_EX_CONTROLPARENT OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR, TO hDlg
            CONTROL ADD BUTTON, hDlg, %IDC_BUTTON1, "Start", 5, 35, 30, 15
            CONTROL ADD BUTTON, hDlg, %IDC_BUTTON2, "Stop",  55, 35, 30, 15
            CONTROL ADD "msctls_progress32", hDlg, %IDC_PROG1, "msctls_progress32_1", 5, 10, 140, 15, %WS_CHILD OR _
                %WS_VISIBLE
            DIALOG SHOW MODAL hDlg, CALL ShowDIALOG1Proc TO lRslt
            FUNCTION = lRslt
        END FUNCTION
        '============================================================================
        FUNCTION PBMAIN()
            InitCommonControls
            ShowDIALOG1 %HWND_DESKTOP
        END FUNCTION
        Last edited by Chris Holbrook; 27 May 2008, 10:22 AM. Reason: removed spurious statemenst

        Comment

        • Michael Mattias
          Member
          • Aug 1998
          • 43447

          #5
          Well, you know what they say: Ugly is in the eye of the beholder.
          Michael Mattias
          Tal Systems (retired)
          Port Washington WI USA
          [email protected]
          http://www.talsystems.com

          Comment

          • Chris Holbrook
            Member
            • Aug 2005
            • 7118

            #6
            Thread eg

            Same application stopping a long running process in a seperate thread.

            Just to show how simple your threads can be in an extreme case!

            Code:
            #COMPILE EXE
            #DIM ALL
            #INCLUDE "WIN32API.INC"
            #INCLUDE "COMMCTRL.INC"
            %IDD_DIALOG1             =  101
            %IDC_BUTTON1             = 1001
            %IDC_BUTTON2             = 1002 '
            %IDC_PROG1 = 1006
            
            GLOBAL gflag AS LONG
            '-------------------------------------------------------------------------------
            SUB long_runner ( hD AS DWORD, t AS DOUBLE)
                LOCAL n, untilnow, tstart AS DOUBLE
            
                tstart = TIMER
                WHILE untilnow < t
                    CONTROL SEND hD, %IDC_PROG1, %PBM_SETPOS, untilnow*100/t, 0
                    IF gflag = 1 THEN
                        ? "interrupted!"
                        EXIT SUB
                    END IF
                    INCR n
                    untilnow = TIMER - tstart
                    SLEEP 50
                WEND
                ? "finished long runner"
            END SUB
            '-------------------------------------------------------------------------------
            FUNCTION wThread ( BYVAL hD AS DWORD) AS LONG
                long_runner ( hD, 20)
            END FUNCTION
            '-------------------------------------------------------------------------------
            CALLBACK FUNCTION ShowDIALOG1Proc()
                LOCAL lresult, n AS LONG
                STATIC hwthread AS LONG
            
                SELECT CASE AS LONG CBMSG
                    CASE %WM_INITDIALOG
                    CASE %WM_COMMAND
                        SELECT CASE AS LONG CBCTL
                            CASE %IDC_BUTTON1
                                IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                                    THREAD CREATE wthread(CBHNDL) TO hwthread
                                    THREAD CLOSE hwThread TO lresult
                                    gflag = 0
                                END IF
                            CASE %IDC_BUTTON2
                                gflag = 1
                        END SELECT
                    CASE %WM_DESTROY
                END SELECT
            END FUNCTION
            '-----------------------------------------------------------------------
            FUNCTION ShowDIALOG1(BYVAL hParent AS DWORD) AS LONG
                LOCAL lRslt AS LONG
                LOCAL hDlg  AS DWORD
            
                DIALOG NEW hParent, "Interrupt a long-running process", 175, 118, 170, 58, %WS_POPUP OR %WS_BORDER OR %WS_DLGFRAME OR _
                    %WS_SYSMENU OR %WS_CLIPSIBLINGS OR %WS_VISIBLE OR %DS_MODALFRAME OR %DS_3DLOOK OR %DS_NOFAILCREATE OR %DS_SETFONT, _
                    %WS_EX_CONTROLPARENT OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR, TO hDlg
                CONTROL ADD BUTTON, hDlg, %IDC_BUTTON1, "Start", 5, 35, 30, 15
                CONTROL ADD BUTTON, hDlg, %IDC_BUTTON2, "Stop",  55, 35, 30, 15
                CONTROL ADD "msctls_progress32", hDlg, %IDC_PROG1, "msctls_progress32_1", 5, 10, 140, 15, %WS_CHILD OR _
                    %WS_VISIBLE
                DIALOG SHOW MODAL hDlg, CALL ShowDIALOG1Proc TO lRslt
                FUNCTION = lRslt
            END FUNCTION
            '============================================================================
            FUNCTION PBMAIN()
                InitCommonControls
                ShowDIALOG1 %HWND_DESKTOP
            END FUNCTION

            Comment

            • Michael Mattias
              Member
              • Aug 1998
              • 43447

              #7
              I forgot the most recent demo.... the 'simplified basic using DDT '....the one I should have done years ago...

              GUI + Worker Thread + Abort Demo 11-24-07

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

              Comment

              • Chris Holbrook
                Member
                • Aug 2005
                • 7118

                #8
                Originally posted by Michael Mattias View Post
                I forgot the most recent demo.... the 'simplified basic using DDT '
                Simplified - I like the sound of that - yes, that's the one!

                Comment

                • Michael Mattias
                  Member
                  • Aug 1998
                  • 43447

                  #9
                  I truly do not believe it gets any simpler than that.. at least to do it without timing and/or "check the value of a GLOBAL variable" loops.

                  And for sure I believe you'll not find a better-commented demo explaining what's happening.

                  Maybe that would be a really good program to compile and attach an 'EXE' for people to run?
                  Michael Mattias
                  Tal Systems (retired)
                  Port Washington WI USA
                  [email protected]
                  http://www.talsystems.com

                  Comment

                  • Chris Holbrook
                    Member
                    • Aug 2005
                    • 7118

                    #10
                    Originally posted by Michael Mattias View Post
                    I truly do not believe it gets any simpler than that.. at least to do it without timing and/or "check the value of a GLOBAL variable" loops.
                    Questions:
                    • why not use a global variable?
                    • why do you consider DIALOG DOEVENTS ugly?
                    • It would be useful to know which bits of DDT work with multithreading and which don't - any ideas?

                    Comment

                    • Michael Mattias
                      Member
                      • Aug 1998
                      • 43447

                      #11
                      I don't like GLOBALs because it makes code less re-useable; and more difficult to port to a separate module (DLL) within the same application. I also tend to forget the names of GLOBAL variables, but that's probably not a real good excuse.

                      DIALOG DOEVENTS is not ugly; looping on a variable's value is ugly: it's inefficient. But since you brought it up, I also consider it ugly to use additional "inline" message loops at multiple points within a program.

                      AFAIK - and I am NOT a 'DDT' guy - there are no restrictions vis-a-vis 'DDT in Multithreaded applications' than there are in general on 'DDT in any application' or on 'multi-threaded applications using the PB compilers.'

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

                      Comment

                      • Chris Holbrook
                        Member
                        • Aug 2005
                        • 7118

                        #12
                        Originally posted by Michael Mattias View Post
                        AFAIK - and I am NOT a 'DDT' guy - there are no restrictions vis-a-vis 'DDT in Multithreaded applications' than there are in general on 'DDT in any application' or on 'multi-threaded applications using the PB compilers.'
                        I was thinking about your comment in the source code of the last example quoyted, where you say

                        ' I had to use a string ptr here instead of CONTROL GET TEXT because the calling thread was in a wait
                        ' state (WaitForSingleObject) and apparently (undocumented but not reasonably) CONTROL GET TEXT does
                        ' "something" which must execute in the context of the same thread (suspended) as the dialog.
                        ' Apparently (that means also not documented), DIALOG GET USER does not need to execute anything in the
                        ' context of the thread in which the dialog was created.

                        Comment

                        • Michael Mattias
                          Member
                          • Aug 1998
                          • 43447

                          #13
                          Um, self-explanatory?

                          Well, Ok, not really all that self-evident... but it does mean that DDT must be doing something to implement CONTROL GET TEXT which results in a WM_GETTEXT message or something else which must execute in the context of the thread in which the window (dialog) was created... but whatever that is cannot get done because that thead is currently suspended on a WFSO.

                          Meaning... CONTROL GET TEXT cannot be used when the thread in which the dialog was created is in a wait state.

                          Like I said... this is not documented where it should be, under the CONTROL GET TEXT statement.


                          Also.... what's left works, don't it?

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

                          Comment

                          • Chris Holbrook
                            Member
                            • Aug 2005
                            • 7118

                            #14
                            Originally posted by Michael Mattias View Post
                            Also.... what's left works, don't it?
                            It certainly seems to. I shall no doubt borrow it when I need to go GLOBAL-free, and include you in my prayers.

                            Think I'll start another thread about DDT and threads.

                            Comment

                            Working...
                            X