Threading questions/problems

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

Re: Threading questions/problems

Postby fxm » Feb 15, 2015 8:19

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
Posts: 9076
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Threading questions/problems

Postby fxm » Feb 15, 2015 9:27

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: 3209
Joined: Jul 28, 2005 14:45
Location: Germany

Re: Threading questions/problems

Postby dkl » Feb 15, 2015 15:18

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

Re: Threading questions/problems

Postby fxm » Feb 15, 2015 17:16

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: 313
Joined: Jan 05, 2014 12:33
Location: Belgium

Re: Threading questions/problems

Postby Provoni » Oct 31, 2016 11:14

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
Posts: 9076
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Threading questions/problems

Postby fxm » Oct 31, 2016 13:40

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: 3190
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Threading questions/problems

Postby MrSwiss » Oct 31, 2016 13:43

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
Posts: 9076
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Threading questions/problems

Postby fxm » Oct 31, 2016 14:38

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: 313
Joined: Jan 05, 2014 12:33
Location: Belgium

Re: Threading questions/problems

Postby Provoni » Nov 01, 2016 10:24

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
Posts: 9076
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Threading questions/problems

Postby fxm » Nov 01, 2016 10:46

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: 3190
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Threading questions/problems

Postby MrSwiss » Nov 01, 2016 12:42

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: 313
Joined: Jan 05, 2014 12:33
Location: Belgium

Re: Threading questions/problems

Postby Provoni » Nov 01, 2016 13:38

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
Posts: 9076
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Threading questions/problems

Postby fxm » Nov 01, 2016 14:12

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: 47
Joined: Jul 04, 2017 18:09

Re: Threading questions/problems

Postby Iczer » Apr 23, 2019 15:11

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() ?

Return to “Beginners”

Who is online

Users browsing this forum: No registered users and 11 guests