Thread updating label

joseluis

Active Member
Licensed User
Longtime User
Hi. I'm trying to understand how to use threads to do some parallel tasks. Specifically drawing a splash screen while loading some datafiles.

Attached is a small example that illustrates some things I don't yet understand:

  1. Why label updates from the thread sub, doesn't happen until the end? And why the text that it prints is always the one from the last call?
  2. It doesn't seem possible to trigger the Ended event before the Create_Activity sub finishes.
  3. What I'm doing wrong?

This is the code
B4X:
Sub Process_Globals
   Dim SplashStillDrawing As Boolean   : SplashStillDrawing = True
   Dim SplashThread As Thread
   Dim SplashLock As Lock
End Sub

Sub Globals
   Dim InfoLabel As Label
   Dim InfoLabelText As StringBuilder : InfoLabelText.Initialize
End Sub

Sub Activity_Create(FirstTime As Boolean)

   If FirstTime Then Activity.Initialize("MainActivity")
   InfoLabel.Initialize("InfoLabel")
   Activity.AddView(InfoLabel, 10%x, 10%y, 80%x, 80%y)
   InfoLabel.Color = Colors.LightGray
   InfoLabel.TextColor = Colors.black
   

   If SplashStillDrawing Then
   
      Log2("splash_YES")
      
      If FirstTime Then
         Log2("splash_FIRSTTIME")
         SplashThread.Initialise("SplashThread")
         SplashLock.Initialize(True)
         
         Dim args(0) As Object
         SplashThread.Start("Create_Splash",args)
         Log2("After calling Create_Splash")
      End If

      ' Do things....

      SplashLock.Wait
      
      Log2("(Splash Finished)")
      Wait(3)
   Else
      Log2("splash_NOT")
   End If ' /SplashStillDrawing
   
   Log2("Thread running:" & SplashThread.Running )
   
   'Wait(2)
   'Log2("Exiting...")
   'Activity.Finish
   'ExitApplication
End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
End Sub

Sub Create_Splash
   
   Dim params(1) As Object
   params(0) = "> Create_Splash" :   SplashThread.RunOnGuiThread("Log2", params)
   
   Wait(3)

   SplashThread.Interrupt
   params(0) = "> interrupted" :   SplashThread.RunOnGuiThread("Log2", params)
   
   SplashLock.Unlock
   params(0) = "> unlocked" :   SplashThread.RunOnGuiThread("Log2", params)
   
   params(0) = "> returning" :   SplashThread.RunOnGuiThread("Log2", params)
   Return
End Sub



Sub SplashThread_Ended(fail As Boolean, error As String)
   Log2("SplashThread_Ended")   
   SplashStillDrawing = False
End Sub



Sub Wait(Seconds As Int)
   Dim Ti As Long
   Ti = DateTime.Now + (Seconds * 1000)
   Do While DateTime.Now < Ti
      DoEvents
   Loop
End Sub

' Logs both to IDE & to Label
Sub Log2(InfoText As String)
   Log(InfoText)
   InfoLabelText.Append(InfoText & CRLF)
   InfoLabel.Text = InfoLabelText.ToString
   DoEvents
End Sub
 

Attachments

  • ThreadsHowto.zip
    5.9 KB · Views: 220
Last edited:

agraham

Expert
Licensed User
Longtime User
This is wrong.
B4X:
Sub Wait(Seconds As Int)
   Dim Ti As Long
   Ti = DateTime.Now + (Seconds * 1000)
   Do While DateTime.Now < Ti
      DoEvents
   Loop
End Sub
You are not letting the main thread get back to the message loop so the queued events are not being processed until you leave this Sub. DoEvents does not fullly pump the message loop. Trying to wait on the main thread by any means is bad programming practice and should be avoided. Use a Timer instead.
 
Upvote 0

joseluis

Active Member
Licensed User
Longtime User
I see. Thanks for the advice. I'll change it to timers and see what happens.

I was using the wait sub for simulating the time that will be spent by the real operations. Hehe.
 
Upvote 0

agraham

Expert
Licensed User
Longtime User
Actually I see now that I was wrong and didn't read your code closely enough. I wrongly assumed that Wait was running on the main thread.

You shouldn't be callling DoEvents in a thread as a thread has no GUI elements. To wait in a thread use Sleep which doesn't use CPU time.

You should not need to call Interrupt on your own thread, just return from the thread Sub to exit the thread.

I don't follow the logic of your example. You must exit Activity_Create in order for messages to be processed in your message loop so you can't do lengthy stuff in Activity_Create and expect a thread to be able to uppdate a GUI element. It is the thread that should be doing the work while the main thread idles in the message loop.
 
Upvote 0

joseluis

Active Member
Licensed User
Longtime User
Well, it works, so I can build over this skeleton now. Thank you very much for the pointers. The logic on the example was confused, because I was. ;) I like the idea you gave me about having the long data load in the new thread, and not in Activity_Create.

Just allow me to ask you one more thing. In your threading example you have this:
B4X:
Sub ThreadSub1
   Dim Count As Int
   Dim Params(1) As Object
   Do While Count < 1000
      Count = Count + 1
      Params(0) = Count 
      PBprog = Count / 10
      ok = False
      Do Until ok
         Thread1.RunOnGuiThread("Update1", Params)
         ok = Lock1.WaitFor(1000)
      Loop
   Loop
End Sub

Sub Update1(count As Int)
   EditText1.Text = count
   PB.Progress = PBprog
   Lock1.Unlock
End Sub
And it works but I'm not sure if I understand the subjacent logic. Must I assume that the command Lock1.WaitFor() locks the semaphore implicitly? If not, how it gets locked each time after Update1 unlocks it?
 
Upvote 0
Top