Threads execute code in the background.
The following class constants can be used to set the priority of the thread with the Priority property.
|NormalPriority||5||Normal priority (same as Main thread)|
These values are totalled, then divided by the main thread and divvied out by proportion, but the main thread always has a priority of five, so using very large numbers for other threads will potentially starve the main thread. For example, if you give a second thread a priority of 500, there will be a total 505 slots in the scheduler and five of those belong to the main thread. Read more about priority below.
The following class constants can be used to test the state the thread is in by examining the value in the State property.
|Running||0||The thread is running normally.|
|Waiting||1||The thread has been blocked by a call to Signal or Enter from one of the locking mechanisms, CriticalSection, Mutex, or Semaphore.|
|Suspended||2||It will not execute because of a call to the Suspend method.|
|Sleeping||3||It has been put to sleep by a call to the Sleep method.|
|NotRunning||4||The thread will be in this state prior to a call to Run or after the thread has finished running.|
Threads stay in scope as long as they are active, even if the variable or property referring to the thread goes out of scope.
Updating the User Interface from a Thread
|Do not access any built-in property or method of a user interface control or Window from within a Thread as platform UI frameworks are not thread-safe. Doing so raises a ThreadAccessingUIException. See below for additional information.|
Your application code normally runs in the so-called main thread which is the thread responsible for managing events in the user interface. If you have a long-running process that runs in the main thread, the user interface will appear to be locked.
You use the Thread class to run code that will not affect the state of the user interface. This also means that code that runs in these threads cannot access the user interface. Doing so raises a ThreadAccessingUIException.
Essentially, a thread is used to allow a very time-consuming process to run in the background. There may be times where you want to update the user interface with something that has been processed in the thread. The way to do this is to have the user interface request the information from the thread.
The simplest technique is to have a Timer on the Window periodically checks a property on the Thread. The Timer then uses this information to update the UI. The example below demonstrates this.
For most usage, we recommend you use the Task class included with the UIThreadingWithTask example (Examples/Desktop/UpdatingUIFromThread/UIThreadingWithTask) when you need to update UI from a Thread. The Task class is a Thread subclass that you can use in place of Thread.
Keep in mind that a Thread can access your own properties and method that are part of UI controls or Windows as long as they do not in turn access the UI.
Safely Sharing Resources Between Threads
Sometimes you may have a resource (data or a file, for example) that needs to be used by multiple threads that are all currently running. An example would be that a thread tries to open a file for writing that another thread has already opened for writing. This can cause issues and unwanted exceptions.
Threads are managed by the built-in Thread Scheduler. Its task is to allocate the CPU's processing cycles among all of the application's threads. Note that because Threads are cooperative, they all share the CPU cycles of a single CPU core. Threads cannot access other CPU cores. The Priority property determines how many or how few processing cycles the Thread gets, relative to the main thread (which always has a priority of 5) and any other Threads that you have created. Use a higher value for Priority to give a thread more CPU cycles and a lower value to give it less CPU cycles.
Threads run in the background, but are temporarily blocked by certain user actions:
- While the mouse button is held down,
- While a window is being dragged,
- While a menu in the menu bar is pulled down,
- While the mouse button is pressed on a control in a window (not true of Windows)
When the Thread Scheduler decides to stop execution of the current thread and allow another thread to run, it is called a context switch. The amount of time a thread runs is called the time slice for the thread.
Threads can yield time to other threads and other applications each time they execute a looping construct such as For, While, and Do. However, a thread does not necessarily yield time at every loop boundary even though it has an opportunity to do so. A thread actually yields to another thread when the Thread Scheduler decides that its timeslice has expired. Context switches are expensive, so the Thread Scheduler always tries to avoid them.
|NOTE: Using the DoEvents method of the Application class from a multithreaded application is not supported and will most likely generate extremely hard to track down errors and crashes in your application. See DoEvents for more information.|
|NOTE: Code that runs in threads does not run faster (in fact such code typically runs more slowly because the CPU time is shared as described above). Putting code in a thread is a way to provide a better user experience by allows tasks to be perceived as running in the background.|
This is the code in the PushButton Action event hander:
Timer1.Mode = Timer.ModeMultiple // periodically check ProgressBar and update the interface
While progressValue < 100 // if not the Maximum...
// recompute progressValue, a property of the Window
progressValue = progressValue + 1
// do nothing for 1/4 second or so so folks can see the actual progress
Dim waitUntil As Integer = Ticks + 15
While ticks < waitUntil
ProgressBar1.Value = ProgressBar1.Maximum
Me.Mode = Timer.ModeOff // turns off the checking/updating
Else // if not maximum
// updates the interface with the current value that was updated by the thread
ProgressBar1.Value = progressValue