Announcement

Collapse
No announcement yet.

Global Variables and THREADS

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

  • Mike Doty
    replied
    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

    Leave a comment:


  • Steve Bouffe
    replied
    Great thanks everyone

    Leave a comment:


  • Mike Doty
    replied
    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

    Leave a comment:


  • Steve Hutchesson
    replied
    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.

    Leave a comment:


  • Steve Bouffe
    replied
    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

    Leave a comment:


  • Knuth Konrad
    replied
    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.

    Leave a comment:


  • Mike Doty
    replied
    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

    Leave a comment:


  • Mike Doty
    replied
    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

    Leave a comment:


  • Steve Hutchesson
    replied
    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.

    Leave a comment:


  • Jerry Wilson
    replied
    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.

    Leave a comment:


  • Stuart McLachlan
    replied
    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.

    Leave a comment:


  • Steve Bouffe
    started a topic Global Variables and THREADS

    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
Working...
X