Announcement

Collapse
No announcement yet.

Global data back and forth with threads

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

  • Tim Lakinir
    replied
    Thank you Sir Stuart

    Leave a comment:


  • Michael Mattias
    replied
    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.

    Leave a comment:


  • Mike Doty
    replied

    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

    Leave a comment:


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



    Leave a comment:


  • Tim Lakinir
    replied
    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?

    Leave a comment:


  • Dale Yarker
    replied
    ((JFI - THREADED was new in PBWin v8 and PBCC v4))

    Leave a comment:


  • Michael Mattias
    replied
    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, 10:54 AM. Reason: Add info re TLS

    Leave a comment:


  • Mike Doty
    replied
    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, 10:21 AM.

    Leave a comment:


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

    Leave a comment:


  • Mike Doty
    started a topic Global data back and forth with threads

    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, 08:32 AM.
Working...
X