Announcement

Collapse
No announcement yet.

Global data back and forth with threads

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

  • Global data back and forth with threads


    Correction 11/12/21 Added SLEEP after THREAD CREATE (required for thread allocation.)
    SLEEP not needed if using WaitForMultipleObjects is used correctly (demonstrated using groups in below post #3.)
    THREADCOUNT can return 0 or another incorrect value if a thread does not allocate using SLEEP.
    The better solution is to use post #3 that uses WaitForSingleObjects.

    Much easier than using pointers.
    Global type array elements can be used and be thread safe
    No need to use pointers, thread safe or critical section and no race conditions.
    Return values from threads can use the thread number as an element of a global array and are also thread safe
    If you don't want to use a TYPE a global array can be used and just pass the element number to process the data in the global(element) array.

    Pass the thread number (1,2, ...) to the thread function and use it as a unique element to return values in a global array.
    Code:
    TYPE RecordType
     one AS STRING * 12
     two AS LONG
    END TYPE
    GLOBAL gsRecord() AS RecordType                    'global type array
    
    GLOBAL gsResult() AS STRING                        'results from threads
    
    FUNCTION PBMAIN AS LONG                            'program name many.bas 11/11/21
    
     LOCAL x,maxthread AS LONG                           '
    
     maxthread = 64
     REDIM gsResult(1 TO maxthread)                    'option array to receive data back
     REDIM gsRecord(1 TO maxthread)                    'values to thread gsPass(threadnum).values
     REDIM hThread (1 TO maxthread) AS LONG            'thread handles
    
     FOR x = 1 TO maxthread                            'for thread loop
      gsRecord(x).one  = USING$("THREAD#",x)           '  first value to thread
      gsRecord(x).two  = x                             '  second value to thread
      THREAD CREATE Many(x) TO hThread(x)              '  create thread
      SLEEP 1                     'added this 11/12/21 to prevent thread from not allocating!!
      THREAD CLOSE hThread(x) TO hThread(x)            '  close thread
     NEXT                                              'next
     DO:SLEEP 50:LOOP UNTIL THREADCOUNT = 1            'wait for threads to finish
    
     ? JOIN$(gsResult(),$TAB),,"Thread safe global type array"  'show values from threads
    
    END FUNCTION
    
    THREAD FUNCTION Many(BYVAL x AS LONG) AS LONG      'echo values back
     gsResult(x)=USING$("&_,#",TRIM$(gsRecord(x).one),gsRecord(x).two)
    END FUNCTION
    Click image for larger version  Name:	threads.png Views:	0 Size:	8.8 KB ID:	812195
    Last edited by Mike Doty; 12 Nov 2021, 09:32 AM.

  • #2
    I agree with that approach Mike, you can go through a nightmare archipeligo of stack calls but its ugly slow code. If a thread must have unique data for something like a web server, you can pass the data as a structure but for most things, globals work better and are more efficient.
    hutch at movsd dot com
    The MASM Forum

    www.masm32.com

    Comment


    • #3
      WaitForMultipleObjects corrects problem
      This code correctly handles waitformultipleobjects with over 63 threads by processing in groups.
      Code in first post could randomly not allocate a thread without a SLEEP statement even with a few threads.
      SLEEP is not needed with this code.
      Code:
      #INCLUDE "win32api.inc"
      TYPE RecordType
       value1 AS LONG
      END TYPE
      GLOBAL gsRecord() AS RecordType                    'type array element to thread
      GLOBAL gsResult() AS STRING                        'results from threads
      
      FUNCTION PBMAIN AS LONG                            'program name many.bas 11/11/21
       LOCAL x,maxthread,testnum AS LONG
       LOCAL StartIndex,ThreadRemain,CurrentGroup AS LONG'waitformultipleobject variables
       LOCAL sResult AS STRING                           'result to display of all loops
      FOR testnum = 1 TO 3                               'test loop
       maxthread = 100
       REDIM gsResult(1 TO maxthread)                    'option array to receive data back
       REDIM gsRecord(1 TO maxthread)                    'values to thread gsPass(threadnum).values
       REDIM hThread (1 TO maxthread) AS LONG            'thread handles
      
       FOR x = 1 TO maxthread                            'for thread loop
        gsRecord(x).value1  = x                          '  threadnum
        THREAD CREATE Many(x) TO hThread(x)              '  create thread
       NEXT
      
       StartIndex   = 1
       ThreadRemain = MaxThread
       DO
         CurrentGroup = MIN(%MAXIMUM_WAIT_OBJECTS, ThreadRemain)
         WaitForMultipleObjects(CurrentGroup,BYVAL VARPTR(hThread(StartIndex)),%TRUE,%INFINITE)
         StartIndex   = StartIndex   + %MAXIMUM_WAIT_OBJECTS
         ThreadRemain = ThreadRemain - %MAXIMUM_WAIT_OBJECTS
       LOOP WHILE ThreadRemain > 0
      
       sResult+= JOIN$(gsResult(),"") + $CR + $CR 'end of a test
      NEXT testnum
       ? sResult,%MB_SYSTEMMODAL,USING$("# Loops of # threads",testnum-1,MaxThread)
      END FUNCTION
      
      THREAD FUNCTION Many(BYVAL x AS LONG) AS LONG      'echo values back
       gsResult(x)=USING$("# ",gsRecord(x).value1)
      END FUNCTION
      Click image for larger version  Name:	many2.png Views:	0 Size:	13.6 KB ID:	812198
      Last edited by Mike Doty; 12 Nov 2021, 11:21 AM.

      Comment


      • #4
        See also: THREADED variable type**. Available since PB 8 or 9.

        See also, THREADSAFE procedure directive*. If you structure your program such that all access to the array data is done by calling this function you will have data integrity.

        Code:
        FUNCTION GetSetArrayElement (GetOrSet&, Array(), elementNo, value ) THREADSAFE
        
               IF GetOrSet& = %GET THEN
                     value =  Array (elementNo)
               ELSEIF GetOrSet  =  %SET THEN
                     Array (ElementNo) = Value
              END IF
              FUNCTION  =  %TRUE   ' success
        END FUNCTION
        Something like that, anyway.

        ** Uses Windows Thread Local Storage (TLS). PB demo of TLS (pre THREADED variable types) at :
        Terminate Worker Threads Using Windows Events (and Using Thread Local Storage) Demo Dec 23 2005

        *Note the THREADSAFE directive uses a semaphore object (WRONG IMO!) and therefore is not suitable for by-design re-entrant operation.
        Last edited by Michael Mattias; 12 Nov 2021, 11:54 AM. Reason: Add info re TLS
        Michael Mattias
        Tal Systems (retired)
        Port Washington WI USA
        [email protected]
        http://www.talsystems.com

        Comment


        • #5
          ((JFI - THREADED was new in PBWin v8 and PBCC v4))
          Dale

          Comment


          • #6
            Hi All, please pardon my ignorance

            I'm not sure what Mike is showing here, does it means that GLOBAL arrays are quite stable and can
            be used in multi threaded programs without loss of memories?

            Comment


            • #7
              Originally posted by Tim Lakinir View Post
              Hi All, please pardon my ignorance

              I'm not sure what Mike is showing here, does it means that GLOBAL arrays are quite stable and can
              be used in multi threaded programs without loss of memories?
              It's just showing that if you have a Global array, there is no problem with different threads addressing THEIR OWN UNIQUE ARRAY ELEMENT..
              It's a very limited use case and doesn't imply that you can share the same VARIABLE (including array elements) safely between threads.

              Effectively, it's giving each thread it's own unique variable that no other thread uses.



              Comment


              • #8

                ThreadWait function

                Wait for more than 64 threads
                No sleep statement needed after thread create statement
                Prevents incorrect threadcount
                Code:
                #INCLUDE "win32api.inc"
                %Maxthread=65
                '_________________________________________________________________________________________________
                FUNCTION PBMAIN AS LONG
                 LOCAL x AS LONG
                 DIM hthread(1 TO %maxthread) AS LONG      'thread handle array
                 FOR x = 1 TO %maxthread                   'start loop
                  THREAD CREATE MyThread(x) TO hthread(x)  ' create thread
                 NEXT                                      'next
                 ThreadWait hthread()                      'wait for all threads to finish
                 FOR x = 1 TO %maxthread                   'thread close loop
                  THREAD CLOSE hthread(x) TO hthread(x)    ' close thread handle
                 NEXT                                      'next
                 ? USING$("Closed # thread handles",%maxthread),,"Done"
                END FUNCTION
                '_________________________________________________________________________________________________
                FUNCTION ThreadWait(hThread() AS LONG) AS LONG
                 LOCAL ret,StartIndex,ThreadRemain,CurrentGroup,maxthread AS LONG
                 maxthread = ARRAYATTR(hThread(),4)  'waitformultiplethreads.bas 11/13/21
                 StartIndex   = 1
                 ThreadRemain = MaxThread
                 DO
                  CurrentGroup = MIN(%MAXIMUM_WAIT_OBJECTS, ThreadRemain)
                  ret = WaitForMultipleObjects(CurrentGroup,BYVAL VARPTR(hThread(StartIndex)),%TRUE,%INFINITE)
                  StartIndex   = StartIndex   + %MAXIMUM_WAIT_OBJECTS
                  ThreadRemain = ThreadRemain - %MAXIMUM_WAIT_OBJECTS
                 LOOP WHILE ThreadRemain > 0
                END FUNCTION
                '_________________________________________________________________________________________________
                THREAD FUNCTION MyThread(BYVAL x AS LONG) AS LONG
                END FUNCTION

                Comment


                • #9
                  does it means that GLOBAL arrays are quite stable and can be used in multi threaded programs without loss of memories?
                  GLOBAL arrays "MAY" be used whenever you want. However, if used in a multi-threaded process (program), special care must be taken to maintain data integrity. If you are not careful you will corrupt the array data. What you are seeing in this thread (no pun intended) are various ways that special care may be implemented.

                  Dealing with asynchronous execution, in which the program code you have written is executing at more than one place ("line of code") at one time, can be tricky to do if you try to share variables or resource use (e.g., a disk file) and requires you carefully plan and implement a strategy for data integrity. This data integrity is not an issue in a single threaded program, since your program will never be trying to read or write the same variable more than once simultaneously.
                  Michael Mattias
                  Tal Systems (retired)
                  Port Washington WI USA
                  [email protected]
                  http://www.talsystems.com

                  Comment


                  • #10
                    Thank you Sir Stuart

                    Comment

                    Working...
                    X