Some code for Windows, to see how its done on Win.
The first code creates 2000 Threads, 1000 for writing and
1000 for reading.
All 2000 Threads access the same string:
Code: Select all
Structure MyThread
mutex.l
msg.l
String$
EndStructure
#StopIt = 1
OpenConsole()
Procedure Thread1(*p.MyThread)
Repeat
WaitForSingleObject_(*p\mutex,#INFINITE) ; wait until object is available
; now access object
*p\String$ = "$"+RSet(Hex(Random($7FFFFFFF)),8,"0")
If *p\msg = #StopIt
Quit = #TRUE
EndIf
ReleaseMutex_(*p\mutex) ; release object
Until Quit
EndProcedure
Procedure Thread2(*p.MyThread)
Repeat
WaitForSingleObject_(*p\mutex,#INFINITE) ; wait until object is available
; now access object
PrintN(*p\String$)
If *p\msg = #StopIt
Quit = #TRUE
EndIf
ReleaseMutex_(*p\mutex) ; release object
Until Quit
EndProcedure
; create Thread with mutex object
x.MyThread
x\mutex = CreateMutex_(0,0,0)
For a = 1 To 1000
CreateThread(@Thread1(),@x) ; write
CreateThread(@Thread2(),@x) ; read
Next
Delay(30000)
For a = 1 To 100
x\msg = #StopIt
Next a
Delay(2000)
Beep_(800,100)
CloseHandle_(x\mutex)
Note: you can use this system with any "object", means
structures, variables, strings, allocated memory, ...
You just assign 1 mutex to every object and use the 2 functions
WaitForSingleObject_() and ReleaseMutex_() around every part
where you access the object.
The second code contains some memory-string functions,
all made thread safe:
Code: Select all
;
; Thread safe string test, by Danilo
;
; first version without thread safety by NicTheQuick
;
Structure _string
mutex.l ; mutex
len.l ; length of string
adr.l ; address of string in memory
EndStructure
;#StrMutex = 0
;#StrLen = 4
;#StrAdr = 8
;#SIZEOF_STROBJ = 12
NewList String_Collector.l() ; string stack
; Procedure StrCpy(*Dest.BYTE,*Src.BYTE)
; ; helper function
; ; String copy
; If *Dest And *Src
; While *Src\b <> 0
; *Dest\b = *Src\b
; *Src + 1
; *Dest + 1
; Wend
; *Dest\b = 0 ; end
; EndIf
; EndProcedure
Procedure StrNewAlloc(*String._STRING,len)
; helper function
; allocate new string in mem and delete old string-mem
If *String
mem = GlobalAlloc_(#GMEM_FIXED|#GMEM_ZEROINIT,len+1)
If mem
GlobalFree_(*String\adr) ; free old String
*String\adr = mem
*String\len = len
EndIf
ProcedureReturn mem
EndIf
EndProcedure
Procedure StrReAlloc(*String._STRING,len)
; helper function
; allocate new string in mem but DONT delete old string-mem!
If *String
mem = GlobalAlloc_(#GMEM_FIXED|#GMEM_ZEROINIT,len+1)
If mem
*String\adr = mem
*String\len = len
EndIf
ProcedureReturn mem
EndIf
EndProcedure
Procedure StrCreate2(chars.s)
Mutex = CreateMutex_(0,0,0)
Debug "Mutex created: "+StrU(Mutex,#LONG)
If Mutex
*NewStr._string = GlobalAlloc_(#GMEM_FIXED|#GMEM_ZEROINIT,SizeOf(_string))
If *NewStr
*NewStr\adr = GlobalAlloc_(#GMEM_FIXED|#GMEM_ZEROINIT,Len(chars))
If *NewStr\adr
If chars
CopyMemory(@chars,*NewStr\adr,Len(chars))
EndIf
*NewStr\mutex = Mutex
*NewStr\len = Len(chars)
AddElement(String_Collector())
String_Collector() = *NewStr
ProcedureReturn *NewStr
Else
CloseHandle_(Mutex)
GlobalFree_(*NewStr)
ProcedureReturn 0
EndIf
Else
CloseHandle_(Mutex)
ProcedureReturn 0
EndIf
Else
ProcedureReturn 0
EndIf
EndProcedure
Procedure StrCreate()
ProcedureReturn StrCreate2("")
EndProcedure
Procedure StrAdr(*String._STRING)
; Get address of string in memory
If *String
WaitForSingleObject_(*String\mutex,#INFINITE)
retval = *String\adr
ReleaseMutex_(*String\mutex)
ProcedureReturn retval
EndIf
EndProcedure
Procedure StrLen(*Str._STRING)
; Get length of string in memory
;
; The length is stored in the string object,
; but could also be: len = ( GlobalSize_(*Str\adr) - 1 )
If *Str
WaitForSingleObject_(*Str\mutex,#INFINITE)
retval = *Str\len
ReleaseMutex_(*Str\mutex)
ProcedureReturn retval
EndIf
EndProcedure
Procedure.s StrLenS(*Str._STRING)
; returns string length as string
ProcedureReturn StrU(StrLen(*Str._STRING),#LONG)
EndProcedure
Procedure.s StrGet(*Str._STRING)
; Get string
If *Str
WaitForSingleObject_(*Str\mutex,#INFINITE)
retval.s = PeekS(*Str\adr)
ReleaseMutex_(*Str\mutex)
ProcedureReturn retval
EndIf
EndProcedure
Procedure StrChgCh(*Dest._STRING,char.s)
; change string to new chars
If *Dest And char
WaitForSingleObject_(*Dest\mutex,#INFINITE)
If StrNewAlloc(*Dest,Len(char)) ; alloc new
CopyMemory(@char,*Dest\adr,Len(char))
retval = *Dest
EndIf
ReleaseMutex_(*Dest\mutex)
EndIf
ProcedureReturn retval
EndProcedure
Procedure StrChg(*Dest._STRING,*Src._STRING)
; change string to new String
If *Dest And *Source
WaitForSingleObject_(*Dest\mutex,#INFINITE)
WaitForSingleObject_(*Src\mutex ,#INFINITE)
If StrNewAlloc(*Dest,*Src\len) ; alloc new
CopyMemory(*Src\adr,*Dest\adr,*Src\len)
retval = *String
EndIf
ReleaseMutex_(*Dest\mutex)
ReleaseMutex_(*Src\mutex)
EndIf
ProcedureReturn retval
EndProcedure
Procedure StrAddCh(*Dest._STRING,char.s)
; add chars to string
If *Dest And char
WaitForSingleObject_(*Dest\mutex,#INFINITE)
oldStr = *Dest\adr
oldLen = *Dest\len
If StrReAlloc(*Dest,oldLen+Len(char)) ; alloc new
CopyMemory(oldStr, *Dest\adr ,oldLen)
CopyMemory(@char ,(*Dest\adr)+oldLen,Len(char)+1)
GlobalFree_(oldStr)
retval = *Dest
EndIf
ReleaseMutex_(*Dest\mutex)
EndIf
EndProcedure
Procedure StrAdd(*Dest._STRING,*Src._STRING)
; add string to string
If *Dest And *Src
WaitForSingleObject_(*Dest\mutex,#INFINITE)
WaitForSingleObject_(*Src\mutex ,#INFINITE)
oldStr = *Dest\adr
oldLen = *Dest\len
If StrReAlloc(*Dest,(*Dest\len) + (*Src\len)) ; alloc new
CopyMemory(oldStr,*Dest\adr,oldLen)
CopyMemory(*Src\adr,(*Dest\adr)+oldLen,*Src\len)
GlobalFree_(oldStr)
retval = *Dest
EndIf
ReleaseMutex_(*Dest\mutex)
ReleaseMutex_(*Src\mutex)
EndIf
EndProcedure
Procedure EndStrings()
; clears and deletes internal string list
ResetList(String_Collector())
Debug "Deleting "+StrU(CountList(String_Collector()),#LONG)+" strings."
While NextElement(String_Collector())
*String._String = String_Collector()
GlobalFree_ (*String\adr)
CloseHandle_(*String\mutex)
GlobalFree_ (*String)
Wend
ClearList(String_Collector())
EndProcedure
;- App Start
Global As, Bs
Global ThreadEnd
Procedure Thread1(thread)
Shared As, Bs
Repeat
StrChgCh(Bs,"CDE")
StrAdd (As,Bs)
;PrintN( StrGet(As) )
Until ThreadEnd
EndProcedure
Procedure Thread2(thread)
Repeat
StrChgCh(Bs,"")
StrAdd (As,Bs)
;Delay(10)
Until ThreadEnd
EndProcedure
As = StrCreate()
Bs = StrCreate2("Hello World!")
OpenConsole()
PrintN( StrLenS(As) )
PrintN( StrGet (As) )
PrintN( StrLenS(Bs) )
PrintN( StrGet (Bs) )
PrintN("")
StrChgCh(As,"Yo! Yo!")
PrintN( StrLenS(As) )
PrintN( StrGet (As) )
StrAddCh(As," - add somthing. ")
PrintN( StrLenS(As) )
PrintN( StrGet (As) )
StrAdd(As,Bs)
PrintN( StrLenS(As) )
PrintN( StrGet (As) )
PrintN(""):PrintN("Starting Multithreading Test. Please press <RETURN>"):Input()
Beep_(800,100)
t1 = CreateThread(@Thread1(),0)
t2 = CreateThread(@Thread2(),0)
;CreateThread(@Thread3(),0)
Delay(10000) ; Run Threads for a while...
;Delay(60000) ; 1 minute
ThreadEnd = #TRUE
Beep_(800,100)
WaitThread(t1)
WaitThread(t2)
PrintN(""):PrintN("End. Please press <RETURN>"):Input()
CloseConsole()
;- App End
Cleanup:
EndStrings()
Maybe it helps if somebody needs thread safety NOW.