Announcement

Collapse
No announcement yet.

Thread anti collision design

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

  • Thread anti collision design

    Using a CriticalSection requires placing the CriticalSection around
    all instances of the global variable (both in the main thread
    and the worker thread.

    If the program was designed from get go this is possible, but
    in this case a worker thread is added and going through many
    many functions looking for all the instances of this particular
    Global (a UDT array in this case) would be a giant pain.

    Unfortunatly the job of the new worker thread is to update the
    global variable.

    Is there a tricky way to accomplish this?

    ------------------
    Kind Regards
    Mike

  • #2
    Why doesn't this crash?

    If MyFunc resides in ONE place in memory then windows must be
    managing the context switching between the threads and
    because two threads cannot run the same function at the same time
    a collision is not possible?

    Does windows get half way through MyFunc for one process, then
    switch context to the the second process and use the second
    process values for the variables then switch back to the first process
    and continue on with its values?

    Is that how Windows manages multiple processes running the SAME
    function at the SAME time?


    Code:
    #COMPILE EXE "Thread.exe"
    
    #INCLUDE     "WIN32API.INC" ' Win API definitions
                  
    
    GLOBAL hDbg AS LONG ' Emergency STOP the threads  
    
    GLOBAL gVar AS EXTENDED
    
    GLOBAL CritSectn AS CRITICAL_SECTION           
                                     
    
    
    '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
    FUNCTION SomeFunc( nSleep AS LONG ) AS LONG
                  
      LOCAL i, j AS LONG
    
        FOR i = 1 TO 100
     
              gVar = gVar + SIN(33)
              gVar = gVar + SIN(33)
              gVar = gVar + COS(33)
              gVar = gVar + COS(33)
              gVar = gVar + COS(33)
              gVar = gVar + COS(33)
              gVar = gVar + COS(33)
              gVar = gVar + SIN(33)
              gVar = gVar + SIN(33)
              gVar = gVar + TAN(33)
              gVar = gVar + TAN(33)
              gVar = gVar + TAN(33)   
              gVar = gVar + TAN(33)
              gVar = gVar + TAN(33)
              gVar = gVar + SIN(33)
              gVar = gVar + SIN(33)
              gVar = gVar + SIN(33)
              gVar = gVar + SIN(33)
    
    '          PRINT #hDbg, "SomeFunc"+STR$(i)
             ' SLEEP nSleep ' allow time for print to complete 
        NEXT
    
    END FUNCTION
    
    
     
    '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
    FUNCTION ThreadOne( BYVAL hMainDlg AS LONG ) AS LONG
        
      LOCAL i AS LONG 
      LOCAL sTemp AS STRING
      LOCAL sMsg  AS STRING
    
        FOR i = 1 TO 100 
              PRINT #hDbg, "Starting thread one Code event="+STR$(i) 
              CALL SomeFunc(15)
              SLEEP 2     
        NEXT 
      
    
    
    END FUNCTION
                      
    
    '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
    FUNCTION ThreadTwo( BYVAL hMainDlg AS LONG ) AS LONG
        
      LOCAL i AS LONG 
      LOCAL sTemp AS STRING
      LOCAL sMsg  AS STRING
    
    
        FOR i = 1 TO 100  
              PRINT #hDbg, "ThreadTwo event="+STR$(i)
              CALL SomeFunc(13)
              SLEEP 3
        NEXT 
      
    
    
    END FUNCTION
    
    
    '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
    CALLBACK FUNCTION MainCB() AS LONG '
                               
      LOCAL RetVal AS LONG
    
    
        SELECT CASE CBMSG 
            CASE %WM_INITDIALOG
    
    
            CASE %WM_COMMAND
                SELECT CASE CBCTL
                    CASE 100 ' Run 
    
    
                        THREAD CREATE ThreadOne(CBHNDL) TO RetVal ' Thread One  
                        THREAD CLOSE RetVal TO RetVal ' Release the handles so  
    
                        THREAD CREATE ThreadTwo(CBHNDL) TO RetVal ' Thread Two 
                        THREAD CLOSE RetVal TO RetVal ' Memory is freed once thread completes 
    
                              
    
                    CASE 200 ' Quit
                        DIALOG END CBHNDL 
    
                END SELECT
        END SELECT
    END FUNCTION
    
    '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
    FUNCTION PBMAIN  
             
      LOCAL hDlg AS LONG  
      LOCAL sPathAndFile AS STRING
      LOCAL zFandP AS ASCIIZ*255   
    
    
    sPathAndFile = "ThrDbg.txt"
    hDbg = FREEFILE '
    OPEN sPathAndFile FOR OUTPUT LOCK WRITE AS hDbg ' PRINT #hDbg, "MetersToFt="+STR$(MetersToFt) 
                                
    
    
    '    InitializeCriticalSection CritSectn
    
            DIALOG NEW 0,                       "       Dual Threads",   260, 60,    210,    106, 0 TO hDlg
            CONTROL ADD BUTTON,   hDlg, 100,                    "Run",    10, 30,      60,    13, 1
            CONTROL ADD BUTTON,   hDlg, 200,                   "Quit",    10, 46,      60,    13, 1
            CONTROL ADD LABEL,    hDlg, 101,       "Thread 1 Display",    08, 70,     200,    12, %SS_CENTER 
            CONTROL ADD LABEL,    hDlg, 102,       "Thread 2 Display",    08, 84,     200,    12, %SS_CENTER  
            CONTROL ADD LABEL,    hDlg, 103,             "Mixed Text",    08, 08,     200,    12, %SS_CENTER
            DIALOG SHOW MODAL     hDlg  CALL MainCB  ' Modal dialog
              
    '    DeleteCriticalSection CritSectn    
    
    PRINT #hDbg,  "All Done"
    
    
        zFandP = sPathAndFile '  
        CALL SHELLEXECUTE( 0, "open", zFandP, BYVAL 0, BYVAL 0, 1 ) ' Show results
        '=====================  
    
    CLOSE hDbg
    
     
    END FUNCTION
    '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
    ------------------
    Kind Regards
    Mike

    Comment


    • #3
      Not sure what you exactly mean, but if two threads call the same sub or function, there is no problem. If a context switch happens inside
      the execution of a function, the thread will resume on the same point where it stopped...
      If multiple threads call the same sub or function, they all get their own copy of that sub. Only problems arise if the subs update global variables.
      It then is possible that thread #1 changes a global var, a context change happens, and thread #2 also changes that var, which can lead to
      processing errors.


      ------------------
      Regards,
      Peter
      Regards,
      Peter

      "Simplicity is a prerequisite for reliability"

      Comment


      • #4
        >if multiple threads call the same sub or function, they all get their own copy of that sub.

        ah ha! i knew it.

        in this thread http://www.powerbasic.com/support/pb...ad.php?t=13383

        are you implying that "otherfunction()" lives in one
        place in memory and is accessed by different threads when they
        have context?

        yes.

        so each thread makes it own copy of the function and executes it
        in which case the content of the function will not collide
        unless there is a global var that is common.

        in which case the example should crash... yet it doesnt?


        ------------------
        kind regards
        mike

        Comment


        • #5
          Well, it won't crash, but you can get unwanted results. Example:

          - Thread #1 is running.
          - It increments a global variable.
          - Now it compares the global var with a value and sees they are equal

          Now a context switch occurs.

          - Thread #2 increments the same global variable.

          Context switchesa back to thread #1.

          - Thread #1 now continues in the assumption that the Global var is equal while that is not the case.
          (Thread #2 incremented the global var...)
          So the following actions in thread #1 are based on incorrect value of the global var.



          ------------------
          Regards,
          Peter
          Regards,
          Peter

          "Simplicity is a prerequisite for reliability"

          Comment


          • #6
            >going through many many functions looking for all the instances of this particular Global (
            >a UDT array in this case) would be a giant pain
            >Is there a tricky way to accomplish this?

            1. Make a GLOBAL search and replace the last time you use a GLOBAL anything.

            2. Never assume you can port a single-threaded application to multi-threaded simply by throwing in a couple of THREAD CREATE statements and critical section objects.


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

            Comment


            • #7
              Ok, so If I got this right

              adding
              EnterCriticalSection CritSectn

              LeaveCriticalSection CritSectn

              At the start and end of the FUNCTION, will mean that both
              instances of the function (thread1 and thread2) will have the
              critical section in it.

              Because both have it, only one can execute at a time.

              So if a context switch occurs halfway through the calculations
              in thread1, thread two will realize it is locked out until the
              critical section end point is reached in thread1.

              At that point I assume windows just switches back to thread1 as
              there is nothing it can do in thread2?




              ------------------
              Kind Regards
              Mike

              Comment


              • #8
                Why doesnt this crash?

                gVar should crash it because both threads are writing and reading
                that memory address at the same time

                if tht doesnt do it, gDone should.. yet it does not crash???

                Code:
                #COMPILE EXE "Thread.exe"
                
                #INCLUDE     "WIN32API.INC" ' Win API definitions
                              
                
                GLOBAL hDbg AS LONG ' Emergency STOP the threads  
                                          
                GLOBAL gDone AS DOUBLE
                
                GLOBAL gVar AS EXTENDED
                
                       
                                                 
                
                '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
                FUNCTION ThreadOne( BYVAL hMainDlg AS LONG ) AS LONG
                
                
                    WHILE gDone = 0
                 
                          gVar = gVar - 33*2 + 33*2 -1
                
                    WEND
                
                END FUNCTION
                                  
                
                '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
                FUNCTION ThreadTwo( BYVAL hMainDlg AS LONG ) AS LONG
                    
                    WHILE gDone = 0
                 
                          gVar = gVar - 33*2 + 33*2 +2
                    WEND
                
                END FUNCTION
                
                
                '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
                CALLBACK FUNCTION MainCB() AS LONG '
                                           
                  LOCAL RetVal AS LONG
                
                
                    SELECT CASE CBMSG 
                        CASE %WM_INITDIALOG
                
                
                        CASE %WM_COMMAND
                            SELECT CASE CBCTL
                                CASE 100 ' Run 
                
                
                                    THREAD CREATE ThreadOne(CBHNDL) TO RetVal ' Thread One  
                                    THREAD CLOSE RetVal TO RetVal ' Release the handles so  
                
                                    THREAD CREATE ThreadTwo(CBHNDL) TO RetVal ' Thread Two 
                                    THREAD CLOSE RetVal TO RetVal ' Memory is freed once thread completes 
                
                                          
                
                                CASE 200 ' Quit
                                    gDone = 1
                                    DIALOG END CBHNDL 
                
                            END SELECT
                    END SELECT
                END FUNCTION
                
                '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
                FUNCTION PBMAIN  
                         
                  LOCAL hDlg AS LONG  
                  LOCAL sPathAndFile AS STRING
                  LOCAL zFandP AS ASCIIZ*255   
                           
                    gVar = 23
                
                        DIALOG NEW 0,                       "       Dual Threads",   260, 60,    210,    106, 0 TO hDlg
                        CONTROL ADD BUTTON,   hDlg, 100,                    "Run",    10, 30,      60,    13, 1
                        CONTROL ADD BUTTON,   hDlg, 200,                   "Quit",    10, 46,      60,    13, 1
                        DIALOG SHOW MODAL     hDlg  CALL MainCB  ' Modal dialog
                            
                  MSGBOX "gVar="+STR$(gVar)
                
                 
                END FUNCTION
                '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
                ------------------
                Kind Regards
                Mike

                Comment


                • #9
                  gVar should crash it because both threads are writing and reading
                  that memory address at the same time
                  No, the threads don't write at the same time. If one thread is running, all other threads are suspended ontil that thread is done, or it's
                  time slice is over. Only problem can be, as I said, that the variable doesn't hold the expected result due to context switching...
                  (But a criticalsection should safeguard that)



                  ------------------
                  Regards,
                  Peter
                  Regards,
                  Peter

                  "Simplicity is a prerequisite for reliability"

                  Comment


                  • #10
                    >Why doesnt this crash?..
                    >gVar should crash ...

                    "crash" = ???????

                    FWIW, it sounds like your program is working exactly as you coded it.

                    And just to correct post #1 here, as you may be starting off missing something..

                    Using a CriticalSection requires placing the CriticalSection around
                    all instances of the global variable (both in the main thread
                    and the worker thread
                    There are no "instances" of "global variables". A 'global' variable is, well, "global" and any reference to it anywhere within the code module refers to the exact same memory location.

                    Unless by "instance" you mean "occurence within the source code", but the word 'instance' is one of those 'loaded' words which should be used carefully.

                    Also, what entering a critical section does (enterCriticalSection) is prevent more than one thread of execution from executing the same code at the same time. If you are updating a global variable in more than one place in your code AND that code is executing in separate threads of execution, unless both such code sections are obtaining ownership of the SAME critical section it's not going to 'protect' anything.

                    What you need to do is put all code which UPDATES that variable into one function. If 'read only' code needs to get the current value of that variable, well, then your design is faulty because by definition if you are updating that variable in a separate thread of execution there is no reliable 'current' value.




                    [This message has been edited by Michael Mattias (edited August 02, 2007).]
                    Michael Mattias
                    Tal Systems (retired)
                    Port Washington WI USA
                    [email protected]
                    http://www.talsystems.com

                    Comment


                    • #11
                      >No, the threads don't write at the same time. If one thread is running, all other threads are suspended ontil that thread is done,
                      Right, but if there is a context switch half way through updating
                      an extended variable shouldnt that cause a crash?
                      Isnt that what all this is about?
                      Are there any ways in which a global variable can cause a crash?

                      ------------------
                      Kind Regards
                      Mike

                      Comment


                      • #12
                        but if there is a context switch half way through updating an extended variable shouldnt that cause a crash?
                        I don't think the OS will issue a context change in the middle of any action...
                        Are there any ways in which a global variable can cause a crash?
                        Sure.
                        Global holds a pointer to a databuffer
                        Thread #1 tests if it's a valid pointer. It is.
                        -- Context change to thread #2 --
                        Thread #2 sets global to zero
                        -- Contect change to thread #1 --
                        Thread #1 now tries to access the databuffer, assuming it's valid and causes a GPF...


                        ------------------
                        Regards,
                        Peter
                        Regards,
                        Peter

                        "Simplicity is a prerequisite for reliability"

                        Comment


                        • #13
                          >I don't think the OS will issue a context change in the middle of any action...

                          If "action" means "single processor instruction" that is correct.

                          However, the compiler can and does translate one little BASIC statement...
                          Code:
                            G_UDT(Z) = X + Y
                          .. into 20 or more machine instructions, and a context switch can occur between any two of them.

                          MCM

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

                          Comment


                          • #14
                            Twenty Opcodes? A slight stretch of the imagination?

                            Even if there were 20, only one (1) is significant in this case.

                            Bob Zale
                            PowerBASIC Inc.


                            ------------------

                            Comment


                            • #15
                              As soon as it's more than one, it may as well be twenty, right?

                              The point being, any single line of BASIC code is not necessarily executed atomically.


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

                              Comment


                              • #16
                                That's true, but it's only the last opcode that counts in an assignment statement of that type.



                                ------------------

                                Comment

                                Working...
                                X