Title: Threading, Synchronizing, And Performing Asynchronous Operations In the 'NET Framework
1Threading, Synchronizing, And Performing
Asynchronous Operations In the .NET Framework
- Hans Verbeeck
- Developer Consultant
- Microsoft EMEA
2Objectives
- Asynchronous execution
- Managing threads
- Synchronization techniques
3Threads
- A thread is an entity that allows your code to
run - allocated face time with the CPU
- has a stack and a private storage area for
variables - confined to a process space
- Each program begins execution with a primary
thread - begins execution at application entry point (e.g.
Sub Main) - shares CPU time with other threads executing on
same machine - can spawn secondary threads within its process
process
Module MyApp ' application entry point '
runs on primary thread Sub Main() ' your
code End Sub End Module
Thread
4Each process has one or more threads
5UI threads
- Primary thread in Windows application is a UI
thread - UI thread listens to internal message loop for
event notifications - UI thread executes form and control event handler
- UI thread in charge of keeping application
responsive to user - Windows Forms are inherently single-threaded
Public Class Form1 Inherits Form Sub
Handler(.. .) Handles cmdExecuteTask.Click
' perform work End Sub End Class
process
UI Thread
6The System.Threading.Thread class
- CLR wraps each OS thread with instance of the
Thread class - allows applications to query and control threads
- reference to current thread acquired using
CurrentThread property - Helpful methods for writing code to test
threading - Thread.Sleep pauses current thread for specified
time - AppDomain.GetCurrentThreadId returns ID of Win32
thread - System.Diagnostics (Process and ProcessThread
classes)
Imports System.Threading Module MyApp Sub
Main() ' get reference to current thread
object Dim MyCurrentThread As Thread
Thread.CurrentThread Dim b As Boolean
MyCurrentThread.IsBackground ' put this
thread to sleep for 5 seconds
Thread.Sleep(5000) ' get Win32 thread ID
of current thread Dim ID As Integer ID
AppDomain.CurrentDomain.GetCurrentThreadId()
End Sub End Module
Win32 Thread
Thread class instance
7DEMO I have a question
8Windows applications and long-running tasks
- Executing long-running tasks on UI thread freezes
application - application cannot respond to events until task
has completed
Public Class Form1 Inherits Form Sub
cmdExecuteTask_Click(ByVal sender As Object, _
ByVal e As EventArgs) _
Handles
cmdExecuteTask.Click ' execute
long-running task Task1(10) End Sub Sub
Task1(ByVal loops As Integer) Dim i As
Integer, message As String For i 1 To
loops ' perform some extended work
Threading.Thread.CurrentThread.Sleep(1000)
' Update UI about status of task message
"Task is " (i / loops).ToString("0.00") "
complete" lblUpdateStatus.Text message
lblUpdateStatus.Update() Next End
Sub End Class
9DEMO The incredibly hanging app
10Motivation for asynchronous execution
- Windows applications may require asynchronous
execution - Application can execute task asynchronously on
worker thread - worker thread can run long-running tasks without
blocking UI - useful for tasks like printing, file I/O,
cross-network calls, etc.
process
UI Thread
WorkerThread
Sub Task1( ) ' perform work End Sub
11Hazards of asynchronous execution(or how an OS
is like a frietkot)
- Asynchronous execution is not always the right
choice - task switching required to share processor is not
free - can actually degrade performance if used
incorrectly - must take explicit steps to protect shared data
- adds to program complexity
- successful test run does not equal program
correctness - timing related issues difficult to uncover or
reproduce - debugging is more difficult
- not all libraries are prepared for concurrent
access - Invest time early evaluating the payoff before
multithreading! - Remember VB.NET components ARE NOT thread safe
by default! - Unlike VB 6 where you code was inherently
protected by the single-threading apartment
12Asynchronous execution
- There are four techniques to execute a method
asynchronously - calling BeginInvoke on a delegate
- using the Threading.ThreadPool class
- using the Threading.Timer class
- creating a new instance of the Threading.Thread
class - First three techniques leverage thread pool
supplied by CLR - makes it fairly easy to run methods asynchronously
process
CLR thread pool
UI Thread
Workerthread
Workerthread
Workerthread
13Delegates defined
- Delegates represent a major abstraction in the
CLR - added to programming model to formalize callback
pattern - a delegate is bound to one or more method
implementations - caller uses delegate to indirectly execute bound
methods - Delegates provide important functionality to .NET
Framework - delegates provide foundation for multicasting of
notifications - delegates provide primary means for asynchronous
execution - delegates provide foundation for CLR events
14Asynchronous execution using a delegate
- Delegates facilitate asynchronous method
execution - BeginInvoke does not block calling thread
- BeginInvoke enqueues request for asynchronous
execution - asynchronous execution handled by pool of worker
threads
Delegate Sub TaskRunnerDelegate(ByVal loop As
Integer) Module MyApp Sub Main() '
create delegate Dim del As TaskRunnerDelegate
del AddressOf Tasks.Task1 ' execute
delegate asynchronously del.BeginInvoke(10,
Nothing, Nothing) End Sub End Module Module
Tasks Sub Task1(ByVal loops As Integer)
' implementation End Sub End Module
UI Thread
Workerthread
15IAsyncResult
- BeginInvoke returns an IAsyncResult object
- object used to manage asynchronous call in
progress - object can be used to poll for completion
Module MyApp Sub Main() ' create
delegate Dim del As TaskRunnerDelegate
AddressOf Tasks.Task1 ' execute delegate
asynchronously Dim ar As IAsyncResult
del.BeginInvoke(10, Nothing, Nothing) Do
Until ar.IsCompleted ' do something
while waiting Loop ' task on secondary
has now completed End Sub End Module
16EndInvoke
- If work is not completed then EndInvoke blocks
calling thread until delegate completes call - object can be used to harvest output parameters
and return value - object can be used to check whether call
experienced exceptions
Delegate Function Delegate1(ByVal x As Integer,
ByRef y As Double) As String Module Tasks
Function Method1(ByVal x As Integer, ByRef y As
Double) As String ' implementation End
Function End Module Module MyApp Sub Main()
Dim del As Delegate1 AddressOf Tasks.Method1
Dim param1 As Integer 10, param2 As Double,
retval As String ' (1) queue up task to
run on worker thread from CLR thread pool Dim
ar As IAsyncResult del.BeginInvoke(param1,
param2, Nothing, Nothing) ' (2) do
something interesting on UI thread ' (3)
attempt to harvest output parameter and return
value Try Dim retval as String
del.EndInvoke(param2, ar) Catch ex As
Exception ' handle cases where
asynchronous task experienced exception End
Try End Sub End Module
17Asynchronous execution using callback object
- You can register callback object for notification
of completion - callback method must be based on AsyncCallback
delegate - callback delegate passed as parameter to
BeginInvoke - callback method run on thread from CLR thread pool
Module MyApp Sub Main() ' create
delegate for asynchronous request Dim del As
TaskRunnerDelegate AddressOf Tasks.Task1
' create delegate callback object Dim cb
As New AsyncCallback(AddressOf MyCallbackMethod1)
' execute delegate asynchronously and
setup callback Dim ar As IAsyncResult
del.BeginInvoke(10, cb, del) End Sub '
method signature conforms to AsyncCallback
delegate Sub MyCallbackMethod1(ByVal ar As
IAsyncResult) Dim del As TaskRunnerDelegate
del CType(ar.AsyncState, TaskRunnerDelegate)
Dim s As String del.EndInvoke(ar) End
Sub End Module
18Raising events asynchronously
- How do you raise an event asynchronously?
- cannot be done using RaiseEvent keyword
- call GetInvocationList and execute BeginIvoke on
each target - synchronizing completion of all notifications far
more tedious
Delegate Sub MyBroadcastDelegate() Class
EventSource1 Event Event1 As
MyBroadcastDelegate ? Sub NotifyAllListers()
' enumerate through each target Dim
target As MyBroadcastDelegate For Each target
In Event1Event.GetInvocationList ' call
BeginInvoke to queue up asynch notification
target.BeginInvoke(Nothing, Nothing) Next
End Sub End Class
19Threading constraints of Windows Forms
- Each form and control is managed by a specific UI
thread - you should never access forms or controls with
any other thread - callback objects should not update controls
directly - Control class provides methods for
thread-switching - Invoke executes method synchronously on control's
thread - BeginInvoke executes method asynchronously on
control's thread - Form and control classes inherit these methods
from Control class - Invoke, BeginInvoke, EndInvoke and CreateGraphics
are safe, all the other methods...arent
20Imports System.Threading Public Class Form1
Inherits Form Delegate Sub TaskRunnerDelegate(B
yVal loops As Integer) Delegate Sub
StatusUpdateDelegate(ByVal StatusMessage As
String) Private Sub cmdExecuteTask_Click( )
Handles cmdExecuteTask.Click Dim del As
TaskRunnerDelegate AddressOf Task1 Dim cb
As New AsyncCallback(AddressOf MyCallbackMethod1)
Dim ar As IAsyncResult del.BeginInvoke(10,
cb, del) End Sub ' task run on worker
thread from CLR thread pool Function
Task1(ByVal loops As Integer) As String '
implementation of long-running task End
Function ' callback method also run on
worker thread from CLR thread pool Sub
MyCallbackMethod1(ByVal ar As IAsyncResult)
Dim UiDelegate As New StatusUpdateDelegate(Address
Of UpdateStatus) Dim args() As String
"Task is complete" Me.Invoke(UiDelegate,
args) End Sub ' code to update form or
controls run on primary UI thread Sub
UpdateStatus(ByVal StatusMessage As String)
lblUpdateStatus.Text StatusMessage
lblUpdateStatus.Update() End Sub End Class
21DEMO A piece of pi
22Using the CLR thread pool directly
- You can directly register callback with the CLR
thread pool - Create delegate instance based on WaitCallback
delegate - Call ThreadPool.QueueUserWorkItem
- Tasks are queued for execution on thread from CLR
thread pool
Imports System.Threading Module MyApp Sub
Main() ' queue up task to run on
secondary thread Dim cb As New
WaitCallback(AddressOf Tasks.MyAsyncProc)
ThreadPool.QueueUserWorkItem(cb, Nothing) End
Sub End Module Module Tasks Sub
MyAsyncProc(ByVal objState As Object) ' do
work on thread from CLR thread pool End Sub End
Module
23DEMO A pool of fractals
24Timers
- System.Windows.Forms.Timer
- implemented using window messages (WM_TIMER)
- timer callback fires only on form thread
- not very precise only fires when message
queue idle - System.Threading.Timer
- thread pool-based implementation
- callback always occurs on thread-pool thread
- can be scheduled for arbitrary start date/time
- System.Timers.Timer
- thread pool-based implementation
- configurable support for callback only on form
thread - supports use in forms designer
25Using System.Threading.Timer
Imports System.Threading Module MyApp1 Sub
Main() ' setup timer callback to fire
once in 3 seconds Dim cb As New
TimerCallback(AddressOf Tasks.MyTimerProc)
Dim tmr As New Timer(cb, Nothing, 3000, 0) End
Sub End Module Module Tasks Sub
MyTimerProc(ByVal objState As Object) ' do
work on thread 2 on timer callback End Sub End
Module
26Using System.Timers.Timer
Imports System.Timers Module Module1 Sub
Main() ' Fire at 1 second intervals (starting 1
second from now). Dim tmr As New
Timer(1000) AddHandler tmr.Elapsed, AddressOf
Tasks.OnTimerFired tmr.Start() ' Do
some work on thread 1 End Sub End
Module Module Tasks Sub OnTimerFired(ByVal
sender As Object, ByVal e As ElapsedEventArgs) Di
m tmr As Timer CType(sender, Timer) ' do work
on thread 2 on timer callback End Sub End
Module
27DEMO Its time for a demo
28Using dedicated threads
- Although preferred for the general case,
thread-pooling techniques are not suited to all
problems - performing very lengthy/blocking operations
- prioritizing multiple tasks relative to each
other - controlling COM apartment affiliation (interop
scenarios only) - want threads existence to hold process running
- System.Threading.Thread supports the creation and
scheduling of dedicated threads - programmer controls thread creation
- programmer controls thread priority
- programmer can suspend/resume thread arbitrarily
- programmer controls apartment affiliation
- programmer controls thread name (aids in
debugging)
29Creating threads with the Thread class
- Threads can be spawned by calling New on Thread
class - constructor requires delegate for parameter-less
Sub procedure - call to Thread.Start creates new Win32 OS-level
thread - call Thread.Join to suspend current thread until
other thread ends - technique does not use the CLR thread pool
Imports System.Threading Module MyApp Sub
Main() Dim t As New Thread(AddressOf
Tasks.MyAsynchTask) t.Start() ' start
secondary task on thread 2 '??? ' do
work in parallel on thread 1 t.Join() '
now wait for thread 2 to end End Sub End
Module Module Tasks Sub MyAsynchTask()
' do work on thread 2 End Sub End Module
30Controlling an executing thread
- Manipulate thread execution with methods on
thread object - Abort raises unrecoverable ThreadAbortException
in thread - Interrupt raises recoverable ThreadInterruptExcept
ion in thread - Sleep pauses the thread for a fixed time
- Suspend pauses thread indefinitely
- Resume resumes execution in a paused thread
- Some aspects of thread control are limited to
safepoints - Some requests may not be enforced immediately
Sub ManipulateThread(t As Thread)
t.Sleep(3000) ' pause thread for 3 seconds
t.Suspend() ' pause thread indefinitely
t.Resume() ' resume execution of paused thread
t.Interrupt() ' raise a trappable error in
thread t.Abort() 'doom thread by raising
unrecoverable error End Sub
31DEMO Working with System.Threading.Thread
We dont encourage it so no demo
32CLR philosophy on threading
- CLR encourages programmers to avoid creating
threads - not required to achieve asynchronous behavior
- not required to achieve parallel execution
- asynchronous delegates are often a much better
choice - Spawning threads explicitly has disadvantages
- requires more work to create and manage threads
- requires more work to join together parallel
tasks - ignores thread pool provided by the CLR
33Comparing asynchronous execution alternatives
- Differences are in terms of scalability and
context - scalable to large work loads?
- CAS markers on initiating thread stack
propagated? - security principal of initiating thread
propagated? - call context of initiating thread propagated?
34CAS Markers
static void baz() static void bar(int x)
(new SomePermission()).Deny() baz() static
void Main() bar(5)
...baz locals... 00000000 SD for baz 00000000
preserved esi 0006ee8c preserved ebp 02d42dc2
bar return address 00000000 (eval
stack)/junk 00000000 SD for bar 00000000
preserved esi 0006eea4 preserved ebp 02d42d4b
Main return address 00000005 arg x 00000000 SD
for main 00000000 preserved esi 0006eec4
preserved ebp
...baz locals... 00000000 SD for baz 00000000
preserved esi 0006ee8c preserved ebp 02d42dc2
bar return address 00000000 (eval
stack)/junk 00b70e60 SD for bar 00000000
preserved esi 0006eea4 preserved ebp 02d42d4b
Main return address 00000005 arg x 00000000 SD
for main 00000000 preserved esi 0006eec4
preserved ebp
FrameSecurityDescriptor
m_assertions
m_denials
m_restrictions
SomePermission
35Synchronization
- An application with multiple threads needs
additional attention - system may switch threads at inopportune times
- two threads may go after same data at same time
- data must be locked (synchronized) to avoid this
- CLR provides multiple synchronization techniques
- SyncBlock in object header can be used to lock an
object - manually synchronization techniques also
available - If no synchronization is applied, default is no
synchronization - leads to unexpected and unpredictable results
- often a poor choice for multithreaded applications
36By default, VB.NET classes are not thread-safe
- Code run in multithreaded environments requires
extra attention - classes and objects must be robust in the face of
concurrency
' this is an example of a class that is not
thread-safe Class Point Private x As Integer
Private y As Integer Sub New(ByVal x As
Integer, ByVal y As Integer) Me.x x
Me.y y End Sub ' method to update point
position Sub SetPointPosition(ByVal x As
Integer, ByVal y As Integer) Me.x x
Me.y y End Sub ' method to read point
position Sub GetPointPosition(ByRef x As
Integer, ByRef y As Integer) x Me.x y
Me.y End Sub End Class
37Inconsistent states and concurrency
- Code running on one thread can leave data in
inconsistent state - code running on other threads is vulnerable to
inconsistent reads - programmers must provide synchronization through
locking
Module MyApp Sub Main() Dim pt As New
Point(10, 10) pt.SetPointPosition(20, 20)
End Sub End Module
Thread
Point
start consistent state
Sub SetPointPosition( ) ' data consistent
at start Me.x x ' data inconsistent in
middle Me.y y ' data consistent at
end End Sub
Point
middle inconsistent state
Point
end consistent state
38Monitors
- CLR provides built-in lock for each reference
type instance - an object's lock is accessible through Monitor
class - resources for lock allocated upon first use
Thread 2
Thread 1
Point
39Monitors and locking
Thread
- Monitor class has methods for locking
- Enter method acquires (or waits for) lock
- Exit method release lock
Point
Time 1 consistent state
Time 2 acquire lock
Point
Time 3 inconsistent state
Sub SetPointPosition( ... ) ' acquire lock
using Monitor Monitor.Enter(Me) '
perform thread safe operations Me.x x Me.y
y ' release lock using Monitor
Monitor.Exit(Me) End Sub
Point
Time 4 consistent state
Time 5 release lock
40A thread-safe class
' this is an example of a thread-safe
class Class Point ' private fields
Private x As Integer Private y As Integer
' update point position Sub
SetPointPosition(ByVal x As Integer, ByVal y As
Integer) Monitor.Enter(Me) Me.x x
Me.y y Monitor.Exit(Me) End Sub '
read point position Sub GetPointPosition(ByRef
x As Integer, ByRef y As Integer)
Monitor.Enter(Me) x Me.x y Me.y
Monitor.Exit(Me) End Sub End Class
41SyncLock
- VB.NET provides the SyncLock statement for
convenience - SyncLock adds code to acquire lock by entering
monitor - SyncLock structures code into Try statement
- SyncLock adds code to call Monitor.Exit in
Finally block
Sub SetPointPosition( ... ) ' you type this
code SyncLock Me Me.x x Me.y y
End SyncLock End Sub
Sub SetPointPosition( ... ) ' VB.NET
produces code like this Monitor.Enter(Me)
Try Me.x x Me.y y Finally
Monitor.Exit(Me) End Try End Sub
42Locking granularity
- Any object can serve as a locking mechanism
- instances of Object class can be created for
additional locks
Class TwoPoints Private x1, y1 As Integer
Private lock1 As New Object() Private x2, y2 As
Integer Private lock2 As New Object() '
update point1 position Sub SetPoint1Position(ByV
al x As Integer, ByVal y As Integer) SyncLock
lock1 Me.x1 x Me.y1 y End
SyncLock End Sub ' read point1 position
Sub GetPoint1Position(ByRef x As Integer, ByRef y
As Integer) SyncLock lock1 x Me.x1
y Me.y1 End SyncLock End Sub '
methods for accessing point2 omitted for
clarity End Class
43ReaderWriterLock
- ReaderWriterLock allows for concurrency with
multiple readers - can perform better than exclusive locking
(monitor and SyncLock)
Class Point Private x, y As Integer Private
lock As New ReaderWriterLock() ' update
point position Sub SetPointPosition(ByVal x As
Integer, ByVal y As Integer)
lock.AcquireWriterLock(5000) Try Me.x
x Me.y y Finally lock.ReleaseWriterL
ock() End Try End Sub ' read point
position Sub GetPointPosition(ByRef x As
Integer, ByRef y As Integer)
lock.AcquireReaderLock(5000) Try x
Me.x y Me.y Finally
lock.ReleaseReaderLock() End Try End
Sub End Class
44Waiting and Pulsing a monitor
- Monitors can block a thread
- Wait method of Monitor blocks current thread
indefinitely - Wait method releases the lock after blocking
thread - Can accrue multiple threads
- Monitors can sequentially release threads
- Pulse method of Monitor releases one blocked
thread - Thread pulsing is FIFO
- PulseAll method releases all threads sequentially
45Using Pulse and Wait methods with a Monitor
Class RequestList Private Shared InnerList As
New Queue() Shared Function GetNextRequest()
As String Threading.Monitor.Enter(InnerList)
If InnerList.Count 0 Then
Threading.Monitor.Wait(InnerList) End If
Threading.Monitor.Exit(InnerList) Return
InnerList.Dequeue() End Function Shared Sub
PostRequest(ByVal req As String)
Threading.Monitor.Enter(InnerList)
InnerList.Enqueue(req) Threading.Monitor.Pulse
(InnerList) Threading.Monitor.Exit(InnerList)
End Sub End Class
46Synchronizing Methods
- Methods can be synchronized as a unit
- MethodImpl attribute in System.Runtime.CompilerSer
vices - Set attribute value to MethodImplOptions.Synchroni
zed - Only one thread enters method at a time
- Can be applied to methods and constructors
- Not applicable to fields or properties
- Can use with both shared and instance
methods/constructors - Does not involve objects SyncBlock
ltMethodImpl(MethodImplOptions.Synchronized)gt
_ Public Shared Sub Foo() MsgBox("only one
call to Foo executes at a time") End Sub
47More Manual Synchronization Options
- Interlocked class
- Lives in System.Threading namespace
- Used for atomic operations on variables
- AutoResetEvent
- Blocks one thread until another thread sets an
event - ManualResetEvent
- Notifies waiting threads of some event
- Mutex
- Mutual exclusion manual blocking mechanism
- MethodImplAttribute
- Similar to a critical section
- A method attribute defined in the
System.Runtime.CompilerServices namespace
48An Alternative use COM
- Use System.EnterpriseServices to do the work for
you - Inherit from ServicedComponent
- Enabled Synchronization, other features
- Configure application to run as a library
application - Advantages
- Microsoft does the dirty work (like STAs)
- You still get to live in the MTA however (unlike
STAs) - Disadvantages
- Will not work on Windows 98, 98SE, or ME
- Prefer Windows 2000 or higher
- Requires Admin privileges for first run if using
XCOPY deployment with auto-install
49COM Interop Compatibility
- By default, .NET threads are COM unaware
- The first call you make via interop requires the
runtime to call CoInitialize on the thread to
setup the COM apartment - If you are going to be calling VB 6-based COM
components (or other STA based components, you
should decorate you main entry point with the
STAThread attribute - If using ASP.NET you want to set the ASPCompat
attribute to true.
50Nice to know...
- Threading is not supported on all clients in the
same way. (Im talking about WIN98 here). - There is a Threading Tutorial in the MSDN help.
- Some cool tools available for free
fromhttp//www.sysinternals.com
51Summary
- Multithreading brings increased capability and
responsibility - Can request threads from existing pool or create
new threads - Thread synchronization controls data access
- Can synchronize with Context or SyncBlock
52(No Transcript)