Running code in a thread
Threads provide a way for you to run code separately from the main thread that is also used by the user interface. They are particularly useful for long-running tasks that might otherwise make your application appear as if it is “frozen” because no user interface updates can occur.
Threads are cooperative, which means two things:
They are relatively easy to use
They only run on a single CPU core
Pre-emptive threading, which has the ability to run your code on multiple CPU cores is much more complex and not something that is supported by Xojo. If you need to run processes on multiple cores, you should consider using separate helper console applications and communicate with them. One technique is shown in the Creating Helper Apps topic.
Creating a Thread
The code that you want to run in the thread is placed in the Run event handler. Anything you call from the thread is considered part of the thread and also runs in the background.
To start a thread you call its Run method, which calls the code in the Run event handler.
By default a thread has a priority of 5. This is the same priority as the main application thread, so if you leave your thread at 5 it will have the same amount of time allocated to it as the main thread.
For example, presume there are 100 “units” of thread time available. If both the main thread and your thread have a priority of 5 then the time unit split is calculated like this:
Total Priority = 5 (main) + 5 (your thread) = 10
Time Units (your thread) = (5/10) * 100 = 50
Time Units (main thread) = (5/10) * 100 = 50
This means that the main thread runs 50 times and your thread runs 50 times. But what if you want your thread to run more often because it is doing some heavy processing? In this case you would increase its priority. If you change your thread’s priority to 15 then the time unit split is calculated the same, but results in more time units for your thread:
Total Priority = 5 (main) + 15 (your thread) = 20
Time Units (your thread) = (15/20) * 100 = 75
Time Units (main thread) = (5/20) * 100 = 25
This means your thread will get 75 of the 100 time units and the main thread will get only 25. So your thread is running 3 times more often than the main thread.
Threads can be slept, suspended, resumed and killed. When you sleep a thread, you specify the amount of time (in milliseconds) for the thread to sleep. It will automatically wake itself when the time has elapsed. If you suspend a thread, it stays suspended until you specifically resume it. Finally you can kill a thread, which terminates it.
Each of these actions changes the state of the thread. You can check the thread state any time using the State property. A thread can be Running (0), Waiting (1), Suspended (2), Sleeping (3) or NotRunning (4).
Communicating with the user interface
Because of operating system restrictions, threads can not directly access or manipulate any user interface element. Should a thread access a UI element your app will raise a ThreadAccessingUIException.
If you have a thread that needs to update user interface in some way, such as updating a Progress Bar, you should instead use a Timer as an intermediary. Rather than having your thread update a Progress Bar directly, a Timer periodically get the progress value from the thread and then the Timer (which runs on the main application UI thread) updates the Progress Bar.
Here is an example of the Run event handler of a thread that was added to a window. The thread loops through an array (with a pause in the middle). The position in the array is stored as a property of the window (ArrayPosition) as is the maximum value of the array (ArraySize):
Var arrayValues() As Integer arrayValues = Array(1, 2, 3, 4, 5, 6, 7, 9, 10) ArraySize = arrayValues.LastRowIndex For i As Integer = 0 To ArraySize ArrayPosition = i App.SleepCurrent(1000) // Pause for 1 second Next
A separate timer can check the value for ArrayPosition and ArraySize and use them to update the Progress Bar. This code is in the Action event handler of a Timer on the window:
If CountThread.State = Thread.NotRunning Then Me.Enabled = False MessageBox("Finished!") Else ThreadProgress.Value = ArrayPosition ThreadProgress.Maximum = ArraySize End If
In the Action event handler of a button on the window, this code starts the thread and the timer:
If CountThread.State = Thread.NotRunning Then CountThread.Run CountTimer.Period = 500 CountTimer.Mode = Timer.ModeMultiple CountTimer.Enabled = True End If
As the Thread runs, it updates the ArrayPosition property on the window. The Timer periodically updates (about every 1/2 a second) the Progress Bar on the window with the value of the property.
This two-step process prevents the thread from directly updating the user interface.
Using the Task class
Included with Xojo is an example of a Task class that can be used to do this as well (Desktop/UpdatingUIFromThread/UIThreadingWithTask). Task is a Thread subclass that has an UpdateUI event handler and method. Use it in place of a Thread when you want your thread to be able to update the user interface. In the Task’s Run event handler, you call the UpdateUI method when you want to update any UI. Then in the UpdateUI event handler, you can have code that directly accesses the UI.
When you call UpdateUI, you can pass either a list of values (using a series of Pair of values. Regardless, in the UpdateUI event handler, you get a dictionary of the values. You can then check the values in the dictionary to determine what to update in the UI.
Examples/Graphics and Multimedia/DrawingWithThreads