Section 15.9. Multithreading with GUIs


15.9. Multithreading with GUIs

The nature of multithreaded programming prevents you from knowing exactly when a thread will execute. GUI controls are not thread safeif multiple threads manipulate a control, the results may not be correct. To ensure that threads manipulate controls in a thread-safe manner, all interactions with them should be performed by the User Interface thread (also known as the UI thread)the thread that creates and maintains the GUI. Class Control provides method Invoke to help with this process. Method Invoke specifies GUI processing statements that the UI thread should execute. The method receives as its arguments a Delegate representing a method that will modify the GUI and an optional array of objects representing the parameters to the method. At some point after Invoke is called, the UI thread will execute the method represented by the Delegate, passing to the contents of the object array as the method's arguments.

Our next example (Figs. 15.1315.14) uses separate threads to modify the content displayed in a Windows GUI. This example also demonstrates how to use thread synchronization to suspend a thread (i.e., temporarily prevent it from executing) and to resume a suspended thread. The GUI for the application contains three Labels and three CheckBoxes. Each thread in the program displays random characters in a particular Label. The user can temporarily suspend a thread by clicking the appropriate CheckBox and can resume the thread's execution by clicking the same CheckBox again.

Figure 15.13. Class RandomLetters outputs random letters and can be suspended.

  1  ' Fig. 15.13: RandomLetters.vb  2  ' Writes a random letter to a label  3  Imports System.Threading  4  5  Public Class RandomLetters  6     Private Shared generator As New Random() ' for random letters       7     Private suspended As Boolean = False ' true if thread is suspended  8     Private output As Label ' Label for output                          9     Private threadName As String ' name of the current thread          10 11     ' constructor 12     Public Sub New(ByVal label As Label) 13        output = label 14     End Sub ' New 15 16     ' delegate that allows method DisplayCharacter to be called 17     ' in the thread that creates and maintains the GUI          18     Delegate Sub DisplayDelegate(ByVal displayChar As Char)     19 20     ' method DisplayCharacter sets the Label's Text property 21     ' in a thread-safe manner                                22     Private Sub DisplayCharacter(ByVal displayChar As Char)  23        ' output character in Label                           24        output.Text = threadName + ": " + displayChar         25     End Sub ' DisplayCharacter                               26 27     ' place random characters in GUI 28     Public Sub Run() 29        ' get name of executing thread 30        threadName = Thread.CurrentThread.Name 31 32        While True ' infinite loop; will be terminated from outside 33           ' sleep for up to 1 second 34           Thread.Sleep(generator.Next(1001)) 35 36           SyncLock Me ' obtain lock                         37              While suspended ' loop until not suspended     38                 Monitor.Wait(Me) ' suspend thread execution 39              End While                                      40           End SyncLock                                      41 42           ' select random uppercase letter 43           Dim displayChar As Char = ChrW(generator.Next(26) + 65) 44 45           ' display character on corresponding Label                       46           output.Invoke(New DisplayDelegate(AddressOf DisplayCharacter), _ 47              New Object() {displayChar})                                   48        End While 49     End Sub ' Run 50 51     ' change the suspended/running state 52     Public Sub Toggle() 53        suspended = Not suspended ' toggle bool controlling state 54 55        ' change label color on suspend/resume 56        If suspended Then 57           output.BackColor = Color.Red 58        Else 59           output.BackColor = Color.LightGreen 60        End If 61 62        SyncLock Me ' obtain lock                    63           If Not suspended Then ' if thread resumed 64              Monitor.Pulse(Me)                      65           End If                                    66        End SyncLock                                 67     End Sub ' Toggle 68  End Class ' RandomLetters 

Figure 15.14. GUIThreads demonstrates multithreading in a GUI application.

  1  ' Fig. 15.14: frmGUIThreads.vb  2  ' Demonstrates using threads in a GUI  3  Imports System.Threading  4  5  Public Class frmGUIThreads  6     Private letter1 As RandomLetters ' first randomLetters object   7     Private letter2 As RandomLetters ' second randomLetters object  8     Private letter3 As RandomLetters ' third randomLetters object   9 10     Private Sub frmGUIThreads_Load(ByVal sender As System.Object, _ 11        ByVal e As System.EventArgs) Handles MyBase.Load 12 13        ' create first thread                            14        letter1 = New RandomLetters(lblThread1)          15        Dim firstThread As New Thread(New ThreadStart( _ 16           AddressOf letter1.Run))                       17        firstThread.Name = "Thread 1"                    18        firstThread.Start()                              19 20        ' create second thread                            21        letter2 = New RandomLetters(lblTread2)            22        Dim secondThread As New Thread(New ThreadStart( _ 23           AddressOf letter2.Run))                        24        secondThread.Name = "Thread 2"                    25        secondThread.Start()                              26 27        ' create third thread                            28        letter3 = New RandomLetters(lblThread3)          29        Dim thirdThread As New Thread(New ThreadStart( _ 30           AddressOf letter3.Run))                       31        thirdThread.Name = "Thread 3"                    32        thirdThread.Start()                              33     End Sub ' frmGUIThreads_Load 34 35     ' close all threads associated with this application 36     Private Sub frmGUIThreads_FormClosing(ByVal sender As System.Object, _ 37        ByVal e As System.Windows.Forms.FormClosingEventArgs) _ 38        Handles MyBase.FormClosing 39        System.Environment.Exit(System.Environment.ExitCode) 40     End Sub ' frmGUIThreads_FormClosing 41 42     Private Sub chkThread_CheckedChanged(ByVal sender As System.Object, _ 43        ByVal e As System.EventArgs) Handles chkThread1.CheckedChanged, _ 44        chkThread3.CheckedChanged, chkThread2.CheckedChanged 45        If sender.Equals(chkThread1) Then     46           letter1.Toggle()                   47        ElseIf sender.Equals(chkThread2) Then 48           letter2.Toggle()                   49        ElseIf sender.Equals(chkThread3) Then 50           letter3.Toggle()                   51        End If                                52     End Sub ' chkThread_CheckedChanged 53  End Class ' frmGUIThreads 

Class RandomLetters (Fig. 15.13) contains method Run (lines 2849), which takes no arguments and does not return any values. Line 30 uses Shared Thread property current-Thread to determine the currently executing thread, then uses the thread's Name property to get the thread's name. Each executing thread is assigned a name that includes the number of the thread in the Main method (see the output of Fig. 15.14). Lines 3248 are an infinite loop, which is in a separate thread from the main thread. When the application window is closed in this example, all the threads created by the main thread are closed as well, including threads (such as this one) that are executing infinite loops. In each iteration of the loop, the thread sleeps for a random interval from 0 to 1 second (line 34).

When the thread awakens, line 36 locks this RandomLetters object. so we can determine whether the thread has been suspended (i.e., the user clicked the corresponding CheckBox). Lines 3739 loop while the Boolean variable suspended remains TRue. Line 38 calls Monitor method Wait on this RandomLetters object to temporarily release the lock and place this thread into the WaitSleepJoin state. When this thread is Pulsed (i.e., the user clicks the corresponding CheckBox again), it moves back to the Running state. If suspended is False, the thread resumes execution. If suspended is still TRue, the loop executes again and the thread re-enters the WaitSleepJoin state.

Line 43 generates a random uppercase character. Lines 4647 call method Invoke passing to it a New DisplayDelegate containing the method DisplayCharacter and a New array of Objects that contains the randomly generated letter. Line 18 declares a Delegate type named DisplayDelegate, which represents methods that take a Char argument and do not return a value. Method DisplayCharacter (lines 2225) meets those requirementsit receives a Char parameter named displayChar and does not return a value. The call to Invoke in lines 4647 will cause the UI thread to call DisplayCharacter with the randomly generated letter as the argument. At that time, line 24 will replace the text in the Label associated with this RandomLetters object with the name of the THRead executing this RandomLetters object's GenerateRandomCharacters method and the randomly generated letter.

When the user clicks the CheckBox to the right of a Label, the corresponding thread should be suspended (temporarily prevented from executing) or resumed (allowed to continue executing). Suspending and resuming a thread can be implemented by using thread synchronization and Monitor methods Wait and Pulse. Lines 5267 declare method Toggle, which will change the suspended/resumed state of the current thread. Line 53 reverses the value of Boolean variable suspended. Lines 5660 change the background color of the Label by assigning a color to Label property BackColor. If the thread is suspended, the background color will be Color.Red. If the thread is running, the background color will be Color.LightGreen. Method Toggle is called from the event handler in Fig. 15.14, so its tasks will be performed in the UI threadthus, there is no need to use Invoke for lines 57 and 59. Line 62 locks this RandomLetters object so we can determine whether the thread should resume execution. If so, line 64 calls method Pulse on this RandomLetters object to alert the thread that was placed in the WaitSleepJoin state by the Wait method call in line 38.

Note that the If statement in line 63 does not have an associated Else. If this condition fails, it means that the thread has just been suspended. When this happens, a thread executing in line 37 will enter the while loop and line 38 will suspend the thread with a call to method Wait.

Class frmGUIThreads (Fig. 15.14) displays three Labels and three CheckBoxes. A separate thread of execution is associated with each Label and CheckBox pair. Each thread randomly displays letters from the alphabet in its corresponding Label object. Lines 14, 21 and 28 create three new RandomLetters objects. Lines 1516, 2223 and 2930 create three new THReads that will execute the RandomLetters objects' GenerateRandomCharacters methods. Lines 17, 24 and 31 assign each Thread a name, and lines 18, 25 and 32 Start the Threads.

If the user clicks the Suspended CheckBox next to a Label, event handler chkThread_CheckedChanged (lines 4252) determines which CheckBox was clicked and calls its associated RandomLetters object's Toggle method to suspend or resume the thread.

Lines 3640 define the frmGUIThreads_FormClosing event handler, which calls method Exit of class System.Environment with the ExitCode property as an argument. This causes all other threads in this application to terminate. Otherwise, only the UI thread would be terminated when the user closes this application; Thread1, Thread2 and Thread3 would continue executing forever.




Visual BasicR 2005 for Programmers. DeitelR Developer Series
Visual Basic 2005 for Programmers (2nd Edition)
ISBN: 013225140X
EAN: 2147483647
Year: 2004
Pages: 435

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net