Imports System.Threading
' The following Imports are not required for ManualResetEvent or
' AutoResetEvent; they merely simplify the code.
Imports System.Windows.Controls
Imports System.Windows.Input
Public Class Example
' mre is used to block and release threads manually.
Private Shared mre As New ManualResetEvent(False)
' The DemoThread method waits on this AutoResetEvent before each step of the
' demo. The MouseLeftButtonUp event handler calls Set to allow each step to
' run.
Private Shared areSyncDemoThread As New AutoResetEvent(False)
' All output is displayed here.
Private Shared outputBlock As TextBlock
' This array of AutoResetEvent objects is used to ensure that all the threads
' created for the first step of the demo are waiting on the ManualResetEvent
' before the step is executed, and to ensure that all the threads are complete
' before going on to the next step.
Private Shared autoResets() As AutoResetEvent = { New AutoResetEvent(False), _
New AutoResetEvent(False), New AutoResetEvent(False) }
' The Shared Demo method starts the thread that controls the demo and hooks
' up the handler for the MouseLeftButtonUp event.
Public Shared Sub Demo(ByVal outputBlock As TextBlock)
Example.outputBlock = outputBlock
outputBlock.Text &= "Click here to begin the demo." & vbLf & vbLf
Dim t As New Thread(AddressOf DemoThread)
t.Start()
AddHandler outputBlock.MouseLeftButtonUp, AddressOf MouseUp
End Sub
' Each time the TextBlock is clicked, the mouse event handler calls Set() on
' the AutoResetEvent, to signal DemoThread to execute the next step of the
' demo. Optionally, it clears the TextBlock.
'
Private Shared clear As Boolean = False
Private Shared Sub MouseUp(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
' If the clear flag has been set, clear the contents of the TextBlock.
If clear Then
outputBlock.Text = ""
clear = False
End If
'outputBlock.Text &= "Click." & vbLf
' Signal the next step of the demo to proceed.
areSyncDemoThread.Set()
End Sub
' Before each step of the demo, DemoThread waits on areSyncDemoThread. When
' the MouseLeftButtonUp event handler signals areSyncDemoThread, DemoThread
' executes the step. Because areSyncDemoThread is an AutoResetEvent, it
' immediately resets after DemoThread is released.
Private Shared Sub DemoThread()
' Wait for a mouse click.
areSyncDemoThread.WaitOne()
' Step 1: Start 3 named threads that block on a ManualResetEvent.
For i As Integer = 0 To 2
Dim t As New Thread(AddressOf ThreadProc)
t.Name = "Thread_" & i
t.Start(autoResets(i))
Next i
' Wait until all three threads have finished displaying their "start"
' messages and called mre.WaitOne().
WaitHandle.WaitAll(autoResets)
outputBlock.Dispatcher.BeginInvoke(displayHelper, vbLf & _
"3 threads are queued, waiting for the ManualResetEvent. Click to signal" & vbLf & _
"the ManualResetEvent by calling its Set() method." & vbLf & vbLf)
' Wait for a mouse click.
areSyncDemoThread.WaitOne()
' Step 2: Call mre.Set() to release the threads.
mre.Set()
' Wait until all three threads have finished displaying their "end"
' messages.
WaitHandle.WaitAll(autoResets)
outputBlock.Dispatcher.BeginInvoke(displayHelper, vbLf & _
"All the threads were released, and the ManualResetEvent remains in the" & vbLf & _
"signaled state. Click to start more threads." & vbLf & vbLf)
' Wait for a mouse click.
areSyncDemoThread.WaitOne()
' Step 3: Show that mre remains signaled by starting more threads. These
' threads will not block, so there is no reason to pass them an
' AutoResetEvent.
Dim twoMoreThreads() As Thread = { New Thread(AddressOf ThreadProc), _
New Thread(AddressOf ThreadProc) }
For i As Integer = 0 To 1
twoMoreThreads(i).Name = "Thread_" & (i + 3)
twoMoreThreads(i).Start(Nothing)
Next i
' Wait until the threads have displayed their messages and finished
' executing.
For Each t As Thread In twoMoreThreads
t.Join()
Next t
outputBlock.Dispatcher.BeginInvoke(displayHelper, vbLf & _
"As long as the ManualResetEvent remains in the signaled state, threads" & vbLf & _
"that wait on it do not block. Click to reset the ManualResetEvent." & vbLf)
' Wait for a mouse click.
areSyncDemoThread.WaitOne()
' Step 4: Demonstrate that Reset puts the ManualResetEvent back into the
' unsignaled state.
outputBlock.Dispatcher.BeginInvoke(displayHelper, vbLf & _
"Calling mre.Reset()." & vbLf & vbLf)
mre.Reset()
' Start a thread that waits on the ManualResetEvent.
Dim t5 As New Thread(AddressOf ThreadProc)
t5.Name = "Thread_5"
Dim wait As New AutoResetEvent(False)
t5.Start(wait)
' Wait until the thread has displayed its message and is waiting.
wait.WaitOne()
outputBlock.Dispatcher.BeginInvoke(displayHelper, vbLf & _
"With the ManualResetEvent in the unsignaled state, threads once again block." & vbLf & _
"Click to release the thread." & vbLf & vbLf)
' Signal the MouseUp event to clear the screen, and wait for a click.
clear = True
areSyncDemoThread.WaitOne()
mre.Set()
t5.Join()
' Step 5a: Signal an AutoResetEvent that does not have a thread waiting on
' it. Note that you can create an AutoResetEvent in the signaled
' state by using New AutoResetEvent(True).
' Put the AutoResetEvent into the signaled state.
wait.Set()
outputBlock.Dispatcher.BeginInvoke(displayHelper, vbLf & _
"If an AutoResetEvent is signaled when there is no thread waiting on it, " & vbLf & _
"the AutoResetEvent remains in the signaled state until a thread waits on" & vbLf & _
"it. That thread is immediately released, and the AutoResetEvent returns" & vbLf & _
"to the unsignaled state. Click here to demonstrate this." & vbLf & vbLf)
' Wait for a click.
areSyncDemoThread.WaitOne()
' Step 5b: Create and release three threads that all wait on the signaled
' AutoResetEvent. Each thread receives an array that contains two
' AutoResetEvent objects, one to signal when the thread is ready
' for release and one to wait on.
' Reset the ManualResetEvent that will synchronize the release of the three
' threads.
mre.Reset()
For i As Integer = 0 To 2
Dim t As New Thread(AddressOf ThreadProcARE)
t.Name = "Thread_" & (i + 6)
t.Start(New AutoResetEvent() { autoResets(i), wait })
Next i
' Wait until all three threads are queued, then release them all at once.
WaitHandle.WaitAll(autoResets)
mre.Set()
' Wait until one thread has been released by the signaled AutoResetEvent and
' has posted its message to the TextBlock.
Dim winner As Integer = WaitHandle.WaitAny(autoResets)
Dim name As String = "Thread_" & (winner + 6)
outputBlock.Dispatcher.BeginInvoke(displayHelper, vbLf & name & _
" was the first thread to wait on the signaled AutoResetEvent. As soon" & vbLf & _
"as " & name & " was released, the AutoResetEvent was reset, blocking the other two" _
& vbLf & "threads. Click to release the waiting threads." & vbLf & vbLf)
' Wait for a click.
areSyncDemoThread.WaitOne()
' Step 6: Release threads and dispose of the Shared resources.
outputBlock.Dispatcher.BeginInvoke(displayHelper, _
"Set() is called twice on the AutoResetEvent, releasing one waiting thread" & vbLf & _
"each time. In order to ensure that both threads are released, a suitable delay" & vbLf & _
"must elapse between calls to Set(). This is accomplished by calling WaitAny()" & vbLf & _
"on the autoResets array." & vbLf & vbLf)
wait.Set()
WaitHandle.WaitAny(autoResets)
wait.Set()
' To ensure that both threads have ended before DemoThread ends, WaitAny()
' is called to wait for the last thread to end. When DemoThread ends, the
' variable 'wait' goes out of scope and the AutoResetEvent it holds is reclaimed
' by garbage collection. If the thread is not released before this, it will not
' be released (and therefore will not run) and it will not be reclaimed by
' collection until the application ends. You might think that the end of
' DemoThread and the end of the application are synonymous, but in fact the
' application is still running as long as the browser window remains active.
' The questions of whether it is important for the last thread to run before
' the application ends, or whether it is a problem to temporarily leak the
' thread, will have different answers from application to application.
WaitHandle.WaitAny(autoResets)
' Dispose of the shared (class-level) wait handles. This is important only
' if the program will go on running, and the wait handles will not be used.
mre.Close()
areSyncDemoThread.Close()
For Each are As AutoResetEvent In autoResets
are.Close()
Next
' Unhook the mouse button event.
outputBlock.Dispatcher.BeginInvoke(unhookHelper)
outputBlock.Dispatcher.BeginInvoke(displayHelper, vbLf & _
"To run the demo again, refresh the page." & vbLf)
End Sub
' Thread Procedures:
' This thread procedure is executed by most of the named threads created in
' this example.
Private Shared Sub ThreadProc(ByVal state As Object)
Dim are As AutoResetEvent = CType(state, AutoResetEvent)
Dim name As String = Thread.CurrentThread.Name
outputBlock.Dispatcher.BeginInvoke(displayHelper, _
name & " starts and calls mre.WaitOne()" & vbLf)
' Signal that the thread is about to wait.
If are IsNot Nothing Then are.Set()
' Wait until the ManualResetEvent is signaled.
mre.WaitOne()
outputBlock.Dispatcher.BeginInvoke(displayHelper, name & " ends." & vbLf)
' Signal that the thread is about to exit.
If are IsNot Nothing Then are.Set()
End Sub
' This thread procedure is used by the last step, which shows the behavior
' of an AutoResetEvent that has been left in the signaled state.
Private Shared Sub ThreadProcARE(ByVal state As Object)
' Get two AutoResetEvent objects, one to signal when this thread is ready
' for release, and one to wait on. The second AutoResetEvent may already
' be in the signaled state.
Dim are() As AutoResetEvent = CType(state, AutoResetEvent())
Dim name As String = Thread.CurrentThread.Name
' Signal that this thread is ready for release.
are(0).Set()
' All threads are started at once, when mre is signaled.
mre.WaitOne()
outputBlock.Dispatcher.BeginInvoke(displayHelper, _
name & " waits on the AutoResetEvent" & vbLf)
' Wait on the previously signaled AutoResetEvent.
are(1).WaitOne()
outputBlock.Dispatcher.BeginInvoke(displayHelper, name & " ends." & vbLf)
' Signal that this thread is ready to end.
are(0).Set()
End Sub
' Helper methods:
' In order to update the TextBlock object, which is on the UI thread, you must
' make a cross-thread call by using the Dispatcher object that is associated
' with the TextBlock. The DisplayOutput helper method and its delegate,
' displayHelper, are used by the BeginInvoke method of the Dispatcher object
' to append text to the TextBlock. UnhookMouseUp and its delegate, unhookHelper,
' unhook the event handler for the MouseLeftButtonUp event.
'
Private Shared displayHelper As New Action(Of String)(AddressOf DisplayOutput)
Private Shared Sub DisplayOutput(ByVal msg As String)
outputBlock.Text &= msg
End Sub
Private Shared unhookHelper As New Action(AddressOf UnhookMouseUp)
Private Shared Sub UnhookMouseUp()
RemoveHandler outputBlock.MouseLeftButtonUp, AddressOf MouseUp
End Sub
End Class
' This example produces output similar to the following:
'
'Click here to begin the demo.
'
'Thread_0 starts and calls mre.WaitOne()
'Thread_1 starts and calls mre.WaitOne()
'Thread_2 starts and calls mre.WaitOne()
'
'3 threads are queued, waiting for the ManualResetEvent. Click to signal
'the ManualResetEvent by calling its Set() method.
'
'Thread_2 ends.
'Thread_1 ends.
'Thread_0 ends.
'
'All the threads were released, and the ManualResetEvent remains in the
'signaled state. Click to start more threads.
'
'Thread_3 starts and calls mre.WaitOne()
'Thread_3 ends.
'Thread_4 starts and calls mre.WaitOne()
'Thread_4 ends.
'
'As long as the ManualResetEvent remains in the signaled state, threads
'that wait on it do not block. Click to reset the ManualResetEvent.
'
'Calling mre.Reset()
'
'Thread_5 starts and calls mre.WaitOne()
'
'With the ManualResetEvent in the unsignaled state, threads once again block.
'Click to release the thread.
'
'Thread_5 ends.
'
'If an AutoResetEvent is signaled when there is no thread waiting on it,
'the AutoResetEvent remains in the signaled state until a thread waits on
'it. That thread is immediately released, and the AutoResetEvent returns
'to the unsignaled state. Click here to demonstrate this.
'
'Thread_7 waits on the AutoResetEvent.
'Thread_7 ends.
'Thread_8 waits on the AutoResetEvent.
'Thread_6 waits on the AutoResetEvent.
'
'Thread_7 was the first thread to wait on the signaled AutoResetEvent. As soon
'as Thread_7 was released, the AutoResetEvent was reset, blocking the other
'two threads. Click to release the waiting threads.
'
'Set() is called twice on the AutoResetEvent, releasing one waiting thread
'each time. In order to ensure that both threads are released, a suitable delay
'must elapse between calls to Set(). This is accomplished by calling WaitAny()
'on the autoResets array.
'
'Thread_8 ends.
'Thread_6 ends.
'
'To run the demo again, refresh the page.