Threading questions/problems

New to FreeBASIC? Post your questions here.
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Threading questions/problems

Post by fxm »

I think that is the same problem (due to ThreadCall):
The pointer value representing the address of 'I' is not preserved until the thread is launched, so a wrong value is passed to the thread ('9' instead of '4239396' in the above example) and the program crashes when it tries to access it!

The ThreadCall problem is general for all types of parameters passed, even by reference!
KeyPgThreadCall ⇒ FxMwikki [Generalized the warning sentence about any type of passed parameter]
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Threading questions/problems

Post by fxm »

dkl, would it be better that I also file a bug report (with a faults example for several passing types) even if you remember well that problem?
dkl
Site Admin
Posts: 3235
Joined: Jul 28, 2005 14:45
Location: Germany

Re: Threading questions/problems

Post by dkl »

Yes, it'd be good to have a bug report for this.
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Threading questions/problems

Post by fxm »

Referring to ThreadCall documentation:
.....
While most subroutines are supported, the following types of subroutines may not be called:
- Subroutines using variable arguments
- Subroutines with unions which are passed Byval
- Subroutines with user types containing unions, arrays, strings, or bitfields which are passed Byval
.....
I am surprised that in fact (after trying) ThreadCall also refuses the subroutines with arguments like described above:
- unions
- user types containing unions, arrays, strings, or bitfields
but even if they are passed Byref !
For now, a workaround is to pass pointers Byval.
Provoni
Posts: 513
Joined: Jan 05, 2014 12:33
Location: Belgium

Re: Threading questions/problems

Post by Provoni »

My program cannot seem to communicate between the main sub and the threads. Here is some example code of the problem, the on screen numbers should be incrementing but are stuck at 1. Can anyone tell me what I am doing wrong and/or provide a simple fix? Please note that I do not like to work with pointers etc.

Thanks

Code: Select all

screenres(800,600,32)

type list
	waiting as integer
	result as integer
end type

dim shared as integer threads=2
dim shared as integer thread_number
dim shared as any ptr thread_ptr(threads)
dim shared as list share(threads)

declare sub main
declare sub active_thread(none as any ptr)

main

sub main
	
	dim as integer i,j,k
	
	for i=1 to threads
		sleep 100
		thread_ptr(i)=threadcreate(@active_thread,0)
	next i
	
	print "press any key to exit"
	
	do
		
		sleep 1000
		
		for i=1 to threads
			
			if share(i).waiting=1 then share(i).waiting=0 'want to tell thread that it can start processing
			print share(i).result,
		
		next i
		print
	
	loop until inkey<>""

end sub

sub active_thread(none as any ptr)
	
	thread_number+=1
	dim as integer local_thread_number=thread_number
	
	do
		
		if share(local_thread_number).waiting=0 then 'start processing
		 	
			sleep 900 'simulating calculations
			
			share(local_thread_number).result+=1 'incrementing
			share(local_thread_number).waiting=1 'want to tell sub main that thread is finished
		
		end if
	
	loop

end sub
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Threading questions/problems

Post by fxm »

Are you sure that is the above code which displays counters stuck at 1?
Even with a poor code like that, the counters should evolve more or less regularly.
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Threading questions/problems

Post by MrSwiss »

Why are you guys never reading DOC?

Example from DOC a little bit enhanced (readability):

Code: Select all

'' Threading synchronization using Mutexes
'' If you comment out the lines containing "MutexLock" and "MutexUnlock",
'' the threads will not be in sync and some of the data may be printed
'' out of place.

Const MAX_THREADS = 10

Dim Shared As Any Ptr ttylock

'' Teletype unfurls some text across the screen at a given location
Sub teletype( ByRef text As String, ByVal x As Integer, ByVal y As Integer )
    ''
    '' This MutexLock makes simultaneously running threads wait for each
    '' other, so only one at a time can continue and print output.
    '' Otherwise, their Locates would interfere, since there is only one
    '' cursor.
    ''
    '' It's impossible to predict the order in which threads will arrive
    '' here and which one will be the first to acquire the lock thus
    '' causing the rest to wait.
    ''
    MutexLock ttylock

    For i As Integer = 0 To (Len(text) - 1)
        Locate x, y + i
        Print Chr(text[i])
        Sleep 25
    Next

    '' MutexUnlock releases the lock and lets other threads acquire it.
    MutexUnlock ttylock
End Sub

Sub thread( ByVal userdata As Any Ptr )
    Dim As Integer id = CInt(userdata)
    teletype "Thread (" & id & ").........", 1 + id, 1
End Sub

' ===== MAIN =====
'' Create a mutex to syncronize the threads
ttylock = MutexCreate()

'' Create child threads
Dim As Any Ptr handles(0 To MAX_THREADS-1)
For i As Integer = 0 To MAX_THREADS-1
    handles(i) = ThreadCreate(@thread, CPtr(Any Ptr, i))
    If handles(i) = 0 Then
        Print "Error creating thread:"; i
        Exit For
    End If
Next

'' This is the main thread. Now wait until all child threads have finished.
For i As Integer = 0 To MAX_THREADS-1
    If handles(i) <> 0 Then
        ThreadWait(handles(i))
    End If
Next

'' Clean up when finished
MutexDestroy(ttylock)
' ===== END MAIN =====	' ----- EOF -----
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Threading questions/problems

Post by fxm »

Better in my opinion, while keeping your poor principle of coding:

Code: Select all

screenres 800,600,32

type list
   waiting as integer
   result as integer
   quit as integer
end type

dim shared as integer threads=2
dim shared as any ptr thread_ptr(1 to threads)
dim shared as list share(1 to threads)

declare sub main
declare sub active_thread(byval p as any ptr)

main

sub main
   
   dim as integer i,j,k
   
   for i=1 to threads
      thread_ptr(i)=threadcreate(@active_thread,cptr(any ptr,i))
   next i
   
   print "press any key to exit"
   
   do
     
      sleep 1000
     
      for i=1 to threads
         
         if share(i).waiting=1 then 'testing if thread task is finished
            print share(i).result;
            share(i).waiting=0      'want to tell thread that it can start processing its task
         end if
         print ,
     
      next i
      print
   
   loop until inkey<>""
   
   for i=1 to threads
      
      share(i).quit=1
      Threadwait(thread_ptr(i))
     
   next i
   
end sub

sub active_thread(byval p as any ptr)
   
   dim as integer k=cint(p)
   
   do
     
      if share(k).waiting=0 then 'start processing task
         
         sleep 900               'simulating calculations
         
         share(k).result+=1      'incrementing
         share(k).waiting=1      'want to tell sub main that thread task is finished
     
      end if
     
      sleep 100
   
   loop until share(k).quit=1    'quit thread

end sub
[edit]
- Added field "quit" for ending thread.
Provoni
Posts: 513
Joined: Jan 05, 2014 12:33
Location: Belgium

Re: Threading questions/problems

Post by Provoni »

Thanks fxm,

Could not figure out why your example was working while mine wasn't. I found the problem but don't understand it. The following example now works, but does not work anymore after changing the line "loop until share(thread_number).quit=1" to "loop"?

Code: Select all

screenres(800,600,32)

type list
	waiting as integer
	result as integer
	quit as integer
end type

dim shared as integer threads=2
dim shared as any ptr thread_ptr(1 to threads)
dim shared as list share(1 to threads)

declare sub main
declare sub active_thread(byval tn as any ptr)

main

sub main
	
	dim as integer i
	
	for i=1 to threads
		thread_ptr(i)=threadcreate(@active_thread,cptr(any ptr,i))
	next i
	
	print "press any key to exit"
	
	do
		
		sleep 1000

		for i=1 to threads
			if share(i).waiting=1 then share(i).waiting=0 'want to tell thread that it can start processing
			print share(i).result,
		next i
		print
	
	loop until inkey<>""
	
	for i=1 to threads
		share(i).quit=1
		threadwait(thread_ptr(i))   
	next i

end sub

sub active_thread(byval tn as any ptr)

	dim as integer thread_number=cint(tn)
	
	do
		
		if share(thread_number).waiting=0 then 'start processing 	
			sleep 900 'simulating calculations	
			share(thread_number).result+=1 'incrementing
			share(thread_number).waiting=1 'want to tell sub main that thread is finished
		end if
	
	loop until share(thread_number).quit=1

end sub
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Threading questions/problems

Post by fxm »

No problem on my PC (double core) when using "loop until share(thread_number).quit=1", except obviously that the program blocks and does not exit when entering a character.

Perhaps add a very small sleep in the loop to prevent to hogging the CPU::

Code: Select all

sub active_thread(byval tn as any ptr)

   dim as integer thread_number=cint(tn)
   
   do
      
      if share(thread_number).waiting=0 then 'start processing    
         sleep 900 'simulating calculations   
         share(thread_number).result+=1 'incrementing
         share(thread_number).waiting=1 'want to tell sub main that thread is finished
      end if
     
    sleep 10
   
   loop ''until share(thread_number).quit=1

end sub
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Threading questions/problems

Post by MrSwiss »

Recoded code from Provoni, to improve readability as well as timing (calling thread-proc.).
Changed types used, and eliminated as many 'globals' (shared) as possible. Removed: Sub main.

Added a certain amount of 'style' to code (aka: first things first, etc.), also added comments:

Code: Select all

Declare Sub active_thread(ByVal p As Any Ptr)   ' the 'p' is optional here

Type list
    As Integer	result
    As Boolean  waiting ' integer --> waste of memory!
    As Boolean  quit    ' integer
End Type

Dim Shared As   list    share(Any)		' global, dynamic array (undefined at present)

' globals
Const                   threads = 2     ' type is: integer (infered from initializer)
ReDim Shared As list    share(1 to threads) ' size dyn. array

' locals
Dim As Any Ptr  thread_ptr(1 to threads) 
Dim As UInteger i       ',j,k --> never used anyway (UInteger is the faster loop-counter!)

'----- prog start -----
ScreenRes 800, 600, 32
Width , 600 \ 16

' start threads
For i = 1 To threads
    thread_ptr(i) = ThreadCreate(@active_thread,CPtr(Any Ptr, i))
Next i

Print "press any key to exit"

' main-loop
Do
    For i = 1 to threads
        If share(i).waiting = TRUE Then 'testing if thread task is finished
            Print share(i).result;
            share(i).waiting = FALSE    'want to tell thread that it can start processing its task
        End if
        Print ,
        Sleep 100                       ' give it a break, before calling other thread!
    Next i
    Print

    Sleep 900
Loop Until Len(InKey())

' stop threads
For i = 1 To threads
    share(i).quit = TRUE
    ThreadWait(thread_ptr(i))
Next i
' ----- prog end -----

' thread-proc.
Sub active_thread(ByVal p As Any Ptr)
    Dim as integer k = cint(p)
   
    Do
        If share(k).waiting = FALSE Then'start processing task
            Sleep 900                   'simulating calculations
            share(k).result += 1        'incrementing
            share(k).waiting = TRUE     'want to tell main that thread task is finished
        End If
    
        Sleep 100
    Loop Until share(k).quit = TRUE       'quit thread
End Sub
' ----- EOF -----
Provoni
Posts: 513
Joined: Jan 05, 2014 12:33
Location: Belgium

Re: Threading questions/problems

Post by Provoni »

fxm wrote:No problem on my PC (double core) when using "loop until share(thread_number).quit=1", except obviously that the program blocks and does not exit when entering a character.
Just tested that it fails to increment with 1.05.0 win64 but not with 1.05.0 win32. On a quad core running Win7.
fxm wrote:Perhaps add a very small sleep in the loop to prevent to hogging the CPU:
This fixes the incrementing problem with 1.05.0 win64.

It did however not fix the issue for my main program, but just resolved that now by removing a small part of code in the main loop of the thread that used a mutex. The code inside the mutex has nothing to do with communication between the main sub and threads.

Thanks for your example and tips MrSwiss.
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Threading questions/problems

Post by fxm »

Provoni wrote:
fxm wrote:No problem on my PC (double core) when using "loop until share(thread_number).quit=1", except obviously that the program blocks and does not exit when entering a character.
Just tested that it fails to increment with 1.05.0 win64 but not with 1.05.0 win32. On a quad core running Win7.
No problem for me both in 32bit and in 64bit with Win7, but with a CORE i5 processor (only double core).
Iczer
Posts: 99
Joined: Jul 04, 2017 18:09

Re: Threading questions/problems

Post by Iczer »

I had some problems passing data to thread started by ThreadCreate() - if I quit calling function right after creating thread, data passed to this thread go bad. So question - is ThreadCreate() returning only after starting thread and safely passing data inside it, or before it?
Example - if i remove Sleep(88,1) in code below, thread receive garbled data:

Code: Select all

Public Function UpdateTaskStart(iID As ULong, psMask As ZString Ptr, byref pwsParamA As WString Ptr, pwsParamB As WString Ptr, iParamC As ULong) As Long Export
	' ----------------------------------------------------------------------------------------
	'
	' ----------------------------------------------------------------------------------------
	Dim tData As tagUpdateTask Ptr = New tagUpdateTask(iID, psMask, pwsParamA, pwsParamB, iParamC)
	' ..................................................................................
	Dim As Any Ptr ThreadHandle = ThreadCreate(@Thread_UpdateTask, tData, iThreadStackSize)
	
	If ThreadHandle = 0 Then
		Print "UpdateTaskStart Thread : start / failed : ";tagUpdateTask->Name
		Return 40400003
	Else
		Sleep(88,1) <---------------------- wait
		ThreadDetach(ThreadHandle)
	End If
	' ----------------------------------------------------------------------------------------
	Return 0
End Function
To get around issue and not wait fixed amount of time, mutex should be better:

Code: Select all

Public Function UpdateTaskStart(iID As ULong, psMask As ZString Ptr, byref pwsParamA As WString Ptr, pwsParamB As WString Ptr, iParamC As ULong) As Long Export
	' ----------------------------------------------------------------------------------------
	'
	' ----------------------------------------------------------------------------------------
	Dim ptData As tagUpdateTask Ptr = New tagUpdateTask(iID, psMask, pwsParamA, pwsParamB, iParamC)
	' ..................................................................................
	MutexLock(GlobalThreadStartMutex)
	' ..................................................................................
	Dim As Any Ptr ThreadHandle = ThreadCreate(@Thread_UpdateTask, ptData, iThreadStackSize)
	
	If ThreadHandle = 0 Then
		MutexUnLock(GlobalThreadStartMutex)
		
		Print "UpdateTaskStart Thread : start / failed : ";tagUpdateTask->Name
		Return 40400003
	Else
		MutexLock(GlobalThreadStartMutex)
			ThreadDetach(ThreadHandle)
		MutexUnLock(GlobalThreadStartMutex)
	End If
	' ----------------------------------------------------------------------------------------
	Return 0
End Function

Sub Thread_UpdateTask(ByVal userdata As Any Ptr)
	
	'-----------------------------------------------------------------------------------------
	Dim As tagUpdateTask Ptr ptData = CPtr(tagUpdateTask Ptr, userdata)
	
	MutexUnLock(GlobalThreadStartMutex)
	'-----------------------------------------------------------------------------------------
	'
	'-----------------------------------------------------------------------------------------
	'
	' calculations...
	'
	'-----------------------------------------------------------------------------------------
	Delete ptData
	'-----------------------------------------------------------------------------------------
End Sub
Or it just bug of ThreadCreate() ?
Post Reply