Announcement

Collapse
No announcement yet.

Global Variables and THREADS

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

  • Global Variables and THREADS

    What's the best way to allow many threads modify a global variable?

    At the moment I have a THREADSAFE SUB which I call from the threads

    Code:
    SUB Modify_Quantity( BYVAL V AS LONG) THREADSAFE
    
    GVar = GVar + V
    
    END SUB

  • #2
    Isn't that the whole purpose of THREADSAFE?

    ISTM that Bob spent some time on making that the best way.
    Any home grown solution is likely to be inferior.

    Comment


    • #3
      Steve: I believe that THREADSAFE (or a CRITICAL SECTION) will protect data in a procedure from being modified during a context switch. But it sounds like you want to safely modify GLOBAL variables when accessed (read/write) from multiple threads. The Modify_Quantity procedure in the first post will probably do that. You may also want to take a look at this forum thread. There are Win32 InterLocked functions that you may find useful - and will probably be faster.

      Comment


      • #4
        Just note that most processors over the last 20 - 30 years will not allow write access to a memory location (variable) by two or more threads, task switching is sequential so as long as the operation to modify a variable easily fits into a normal time slice, it will work fine. Most mnemonics like ADD, SUB, MOV have a lock capacity that prevents parallel access. If you are performing complex operations that are longer than a time slice then it makes sense to use any of the thread synchronisation functions available but it can be a bit big and clunky for modifying a single variable.
        hutch at movsd dot com
        The MASM Forum

        www.masm32.com

        Comment


        • #5
          Globals (even if just 1) must be protected.
          This innocent looking code will produce the wrong result due to a SLEEP.

          I would take the extra effort of using a critical section over THREADSAFE
          because if is not affected by sleep, doevents, recursive,msgbox.
          Note: IO functions like open, put, get are not thread safe.

          Code:
          'result should be 100
          %contextswitch=1
          
          #DIM ALL
          #INCLUDE "win32api.inc"
          
          GLOBAL gVar AS LONG
          FUNCTION PBMAIN () AS LONG
           LOCAL x AS LONG
           DIM hThread(1 TO 10) AS LONG
           FOR x& = 1 TO 10
            THREAD CREATE MyThread(0) TO hThread(x)
           NEXT
           WaitForMultipleObjects 10,hThread(1),%True,%INFINITE
           ? USING$("GVar #",gVar)
           FOR x& = 1 TO 100
            THREAD CLOSE hThread(x&) TO hThread(x&)
           NEXT
          END FUNCTION
          
          THREAD FUNCTION MyThread(BYVAL x AS LONG) AS LONG
           LOCAL v AS LONG
           FOR x = 1 TO 10
            v& = 1
            Modify_Quantity(v)
            IF %ContextSwitch THEN SLEEP 1
           NEXT
          END FUNCTION
          
          SUB Modify_Quantity( BYVAL V AS LONG) 'THREADSAFE
            GVar = GVar + V
          END SUB
          https://duckduckgo.com instead of google

          Comment


          • #6
            Here is another technique that will fail by putting the result of each thread into a unique global element.
            The reason it works is because a context-switch is never normally tested like in this example.
            Debugging this would be a nightmare.
            Code:
            #DIM ALL
            GLOBAL gBegin,g() AS LONG
            
            FUNCTION PBMAIN () AS LONG
             LOCAL x AS LONG
             DIM hThread(1 TO 10) AS LONG
             DIM g(1 TO 10)
             FOR x = 1 TO 10
              THREAD CREATE MyThread(0) TO hThread(x)
              SLEEP 50
             NEXT
             ? "Threads have allocated"
             gBegin=1
             DO:SLEEP 10:LOOP UNTIL THREADCOUNT = 1
             ? USING$("#  #  #  #  #  #  #  #  #  #",g(1),g(2),g(3),g(4),g(5),g(6),g(7),g(8),g(9),g(10))
             FOR x& = 1 TO 10
              THREAD CLOSE hThread(x) TO hThread(x)
             NEXT
            END FUNCTION
            
            THREAD FUNCTION MyThread(BYVAL x AS LONG) AS LONG
             DO:SLEEP 10:LOOP UNTIL gBegin
             LOCAL v AS LONG
             FOR x = 1 TO 10
              v = 1
              Modify_Quantity(x,v)
              SLEEP 1  'cause failure by context-switch
             NEXT
            END FUNCTION
            
            SUB Modify_Quantity(ThreadNum AS LONG, V AS LONG)
              g(ThreadNum) = g(ThreadNum) + V
            END SUB
            https://duckduckgo.com instead of google

            Comment


            • #7
              Originally posted by Stuart McLachlan View Post
              Isn't that the whole purpose of THREADSAFE?
              Yepp. Looks exactly the way PB intends it to be used.

              Comment


              • #8
                If I have read the suggested post correctly here and want to know if this is correct?

                https://forum.powerbasic.com/forum/u...n-is-this-safe


                Code:
                GLOBAL gCS AS CRITICAL_SECTION
                
                FUNCTION PBMAIN AS LONG
                
                InitializeCriticalSection gCS
                
                ...............
                
                DeleteCriticalSection gCS
                
                END FUNCTION
                
                THREAD FUNCTION ......
                
                EnterCriticalSection gCS
                
                Make changes to variables
                
                LeaveCriticalSection gCS
                
                END FUNCTION

                Comment


                • #9
                  I have an example written in 64 bit MASM that starts 8 threads, runs each thread with a fixed delay then on thread exit, it updates a thread counter, a single variable until the counter equals the thread count and it never fails. The 8 threads call the same thread proc and it uses a spinlock to separate the threads so the arguments are passed before the next thread starts. It uses the API CreateThread() rather than any high level alternative.
                  hutch at movsd dot com
                  The MASM Forum

                  www.masm32.com

                  Comment


                  • #10
                    Steve,
                    Yes, looks correct.
                    This can also be done, not a suggestion to do so.
                    I always like to bring up I/O functions are not thread safe, learned from Michael Mattias.
                    Code:
                    GLOBAL gCS AS CRITICAL_SECTION
                    
                    FUNCTION PBMAIN AS LONG
                     InitializeCriticalSection gCS
                     THREAD CREATE MyThread
                     DeleteCriticalSection gCS
                    END FUNCTION
                    
                    THREAD FUNCTION MYThread()
                     EnterCriticalSection gCS
                     OpenFiles
                     WriteFiles
                     IncrementGlobals
                     LeaveCriticalSection gCS
                    END FUNCTION
                    
                    SUB OpenFiles
                    END SUB
                    
                    SUB WriteFiles
                    END SUB
                    
                    SUB IncrementGlobals
                    END SUB
                    https://duckduckgo.com instead of google

                    Comment


                    • #11
                      Great thanks everyone

                      Comment


                      • #12
                        In the above thread 6 the unique thread number was not being passed to the array.
                        A thread can safely use a unique element of a global array if no other thread uses it.
                        Also notice I used WaitForMultipleObjects to avoid possibly millions of context-switches in a real program.

                        Notice there is no THREADSAFE or Critical_Section
                        Code:
                        #DIM ALL
                        #INCLUDE "win32api.inc"
                        GLOBAL g() AS LONG
                        FUNCTION PBMAIN () AS LONG
                         LOCAL x,result AS LONG
                         DIM hThread(0 TO 9) AS LONG
                         DIM g(0 TO 9)
                         FOR x = 0 TO 9
                          THREAD CREATE MyThread(x) TO hThread(x)
                         NEXT
                         WaitForMultipleObjects 10,hThread(0),%FALSE,%INFINITE
                         ? USING$("#  #  #  #  #  #  #  #  #  #",g(0),g(1),g(2),g(3),g(4),g(5),g(6),g(7),g(8),g(9))
                         FOR x = 0 TO 9
                          THREAD CLOSE hThread(x) TO hThread(x)
                         NEXT
                        END FUNCTION
                        
                        THREAD FUNCTION MyThread(BYVAL ThreadNum AS LONG) AS LONG
                         LOCAL v AS LONG
                         LOCAL x AS LONG
                         v = 1
                         FOR x = 0 TO 9
                          Modify_Quantity(ThreadNum,v)
                          SLEEP 1                                              'no-failure by context-switch
                         NEXT
                        END FUNCTION
                        
                        SUB Modify_Quantity(ThreadNum AS LONG, V AS LONG)
                          g(ThreadNum) = g(ThreadNum) + V
                          SLEEP 1                                              'no-failure by context-switch
                        END SUB
                        https://duckduckgo.com instead of google

                        Comment

                        Working...
                        X