Updating progress indicators during heavy processing

This is where you could use a timer to iterate through the records, and update the progress bar too.

Instead of using a for next or a while wend loop, you would use the timer.

It would mean keeping code 1-2 the same, then hand execution to the timer in which the 3. part of the code takes place.

Then probably hand execution to a method when the timer finishes, to take care of the last part of your code, after 3.

Never said you have to leave the Thread. Let it do it’s work and from within the Thread, you start from time to time a Timer. :wink:

I should really do a Timer-based Task class…

Hi again

Sorry to labour this point, I must be a bit slow here.
I have implemented the UI Update Task class that is provided in the Examples. The task runs great and updates the UI just fine.

My problem is still that the Task surrenders control back to the main loop from time to time (as it should I realise), and the main loop then carries on trying to process the next export file that the user selected. Which causes issues due to the first one still not being complete in the thread.

I have been trying to use the Thread.State value from the main loop checking if the thread has finished (= 4) but I cant see how to make the main loop NOT continue when it gets control back from the thread IF the Thread.State is not finished.

Tried putting this as the next bit of code after the thread is run

While tskTest.State <> 4 Wend

But the app just hangs. I am guessing that once the thread surrenders control and it gets back to the main app, this wend loop runs and prevents the thread from getting control back at all.

I am getting close to giving up on this and just going back to .NET. Pity cos I really wanted to be a able to run this an a Mac as well.

Any ideas please?
Thanks
Mark

This stuff is tricky to get right. Make us an example and then we could have a look.

[quote=452987:@Mark Ogier]Tried putting this as the next bit of code after the thread is run

While tskTest.State <> 4
Wend
But the app just hangs. I am guessing that once the thread surrenders control and it gets back to the main app, this wend loop runs and prevents the thread from getting control back at all.[/quote]
Correct.

[quote=452804:@Kem Tekinay]You should have your Thread raise an event when it’s done[/quote] and then start a new thread to export the next file.

Thanks for the idea.

Yes I see what you are saying but my issue is that I am calling the thread from within a loop of selected items (IE the user chooses to generate 4 export files). I iterate through the loop of selected files. For each selected file I run the thread which does the export and keeps the UI updated.

BUT

Whilst running the export 1 in a thread, the thread surrenders, control comes back to the main app (my loop) and the next export in the loop gets kicked off. This is what I need to stop.

Having the Thread raise an event to say it is done does not stop the main app continuing through my loop when it gets control back from the thread. I can check the value of a boolean that is set in the event I create for when the task is done. But how do I make the main app act on that such that is waits until the boolean says the thread is finished?

I need to either

  1. Force the thread to finish before it surrenders control

OR

  1. Have the main app not continue when it gets control back unless the thread has finished.

To be clear, I am not interested in this being a multi-tasking app. The only reason I am using a thread (Task) is to keep the UI refreshed with progress information. I dont want the user to be able to do anything else until the exports are completed. Tis is a database app and the last thing I want is users being able to mess with data whilst it is being exported or imported.

Hope thats clear

Many thanks for your help
Mark

Hey

Thanks for helping. This is quite a big and complex app now and has connections to numerous DBs which of course you won’t have, so i will try and flesh something out in pseudo-code. Hope that helps.

  1. User is presented with a list (in a listbox with checkbox in a cell) of export options, about 5 at present, will be more in future.

  2. The user ticks the exports they want to do then presses the go button.

  3. In the Action event of the button is the main loop. It iterates through each row in the list box in a For loop. If the Check box of that row is selected, an export is needed and the Export function is called. It is the Export function that is in the Run of the Task/Thread.

Here is the Pseudo-code

[code]For L = 1 to Listbox.List Count

If ListBox.Cellstate = Checked Then

Select Case ListBox.ExportType


Case "Export1"
  Task.ExportType = "Export1"    // This is a property of the Task I have added to my Task. 
                                                        // My Task is a sub-class of the example Task from Xojo
  Task.Run


Case "Export2"
  Task.ExportType = "Export2"    
  Task.Run

Case "Export3"
  Task.ExportType = "Export3"    
  Task.Run


Case "Export4"
  Task.ExportType = "Export4"    
  Task.Run


Case "Export5"
  Task.ExportType = "Export5"    
  Task.Run



End Select

End If
Next L

[/code]

What happens is that the code drops into the first case, runs the Task/Thread for Export 1, The Export 1 Task surrenders, flow drops back to the main app, the next loop iteration runs, the next case statement is run and the next export starts, all before export 1 has finished.

I need each export to complete sequentially. I am only using Tasks/Threads to faciltate updating the UI in the Task.

Here is the Task.Run Pseudo-Code function

While Not Recordset.EOF   //The recordset is a property of my Task, set in the loop above

  Select Case Me.ExportType

  Case "Export1"
    DataRowString = FormatExport1DataRow(Me.Recordset)
    Me.UpdateUI(RecordNumber)   //Updates the progress indicators

  Case "Export2"
    DataRowString = FormatExport2DataRow(Me.Recordset)
    Me.UpdateUI(RecordNumber)   //Updates the progress indicators

  Case "Export3"
    DataRowString = FormatExport3DataRow(Me.Recordset)
    Me.UpdateUI(RecordNumber)   //Updates the progress indicators

  ....etc

  End Select

    Me.OutputStream.WriteLine(DataRow)

  Me.Recordset.Movenext
Wend



Me.OutputStream.Close
Me.Recordset.Close

I dont claim this to be the best code structure, I am still finding my way with Xojo and Threads. But I dont see any major issues, please correct me :slight_smile:

So any help on this would be great.

As I mentioned above, it seems to me I need to achieve either

  1. Force the thread to finish before it surrenders control

OR

  1. Have the main app not continue when it gets control back unless the thread has finished.

Or some other solution that will work.

Still cant believe all this work just to update a blo**y progress bar! :slight_smile:

thank
Mark

I can’t write much today but here’s a short concept which may bring fresh ideas to your processes (excuse my poor english please):

[code]In the Window:

  1. Each Time the User changes the selection within the Listbox set a Boolean (Private Window Property) to True

In the Action Event of your Button:

  1. Disable the ListBox to prevent the User from changing the selection right now.
  2. Go through your List an build up an Array (Private Window Property) with a unique identifier (native File Path of the File?) of each selected Row
  3. Set the Boolean Window Property to False
  4. Enable the Listbox
  5. Start your Thread

In the Thread :

  1. Check if UBound(Array) > -1
    Pull the 1st (0) entry from your Array
    else
    Exit the Thread gracefully
  2. Process the Export?
  3. If the Window Boolean is still = False
    Remove the 1st entry from the Array
    else
    Remove all entrys from your array and exit the Thread gracefully
  4. Start a Timer (Private Window Property)

In the Timer:

  1. Do your UI and other stuff
  2. Run the Thread again[/code]

You’re looking at the problem linearly, and I don’t blame you. It’s likely how you were taught, it’s how we were all taught, but this requires a new approach, so some clarification is in order.

Xojo’s Main Thread is an invisible event loop that does all the heavy lifting in your app. When you choose something from a menu or implement the Action event of a Pushbutton, that code all runs in the Main Thread.

Any other Threads you create will run concurrently with the Main Thread. Once started, they go off and complete their task, then end. If you put an endless loop into a Thread, it will never end, but will never tie up the Main Thread either. Once started (“Run”), a Thread returns flow to the calling Thread almost immediately.

On the other hand, any loop you put into the Main Thread will tie up the Main Thread, and thus most of your app, until the loop ends. If you create a loop that starts a Thread and then waits for it to finish before moving on, you’ve defeated the purpose of the Thread and your UI will still be locked up.

This is linear (or as you put it, sequential) programming, but the way to attack this problem is through event-driven code. You don’t keep checking if the Thread is finished to move on, you wait until the Thread tell you it’s finished, then do the next thing.

In this case, you want part of the UI to be responsive (progress bars), but not other parts (buttons, menus, etc.), but that’s not how it works, it’s either all or nothing. But you can still get the results you seek with a little extra effort.

Here’s how I’d handle it:

  • Create a Task subclass designed to do this export. It will contain all the information is needs to complete its task. That can include an array of files to export.
  • When the user clicks export, store this information in the Task’s properties, disable the interface, then run the Task.
  • React to the Task’s events to update your progress bar. When it tells you it’s done, re-enable the interface.

At no time do you create a loop to monitor the Task, you let it feed you the information instead.

If you don’t want to handle disabling the interface, move this function to a dialog window and have it do the processing in the way I described. When the Task is finished, it will close the dialog.

Mark, you are describing properly what your problem is, and now you need to implement the ideas you have been given here. So, instead of [quote=452991:@Mark Ogier]I need to either

  1. Force the thread to finish before it surrenders control

OR

  1. Have the main app not continue when it gets control back unless the thread has finished.[/quote] which will not work, you will need to do something like

[quote=452930:@Michel Bujardet]This is where you could use a timer to iterate through the records, and update the progress bar too.

Instead of using a for next or a while wend loop, you would use the timer.

It would mean keeping code 1-2 the same, then hand execution to the timer in which the 3. part of the code takes place.

Then probably hand execution to a method when the timer finishes, to take care of the last part of your code, after 3.[/quote]

I have just quoted one of the valid ideas you were given, I am not saying others aren’t as valid (or even better).

As an aside, I’ve create a class I call “Looper” that uses a Timer in the Main Thread to emulate processing in a Thread. It’s designed around a loop and its main advantage over the Task example is that it still provides full access to the UI. Now I just have to decide how best to distribute this.

github :stuck_out_tongue:

Yeah, but it’s such a simple class. grumble

sure … but you’ll want to place it in a simple demo, have a license, etc and that makes sense as a github project
no ?

This is getting bit off-topic and I didn’t mean to hijack this conversation. In short, yes, all that makes sense, but I could just as easily put it on my web site since I don’t really expect it to change.

+1 GitHub

Fine, you taskmasters.

See:

https://github.com/ktekinay/Xojo-Looper

@Mark Ogier , take a look to see if that helps you.

Hey Kem, thanks for putting your project on GitHub.

Just took a look. I have no idea how to load all this into a Mac Desktop project. Never had raw source like this to load. How do I do that so I can load it up and check the mysteries of threading?

Cheers
Mark