Geeks With Blogs
A Technical Debtor Toward continuous improvement
System.Threading namespace is the typical starting point for threading.

Quick demo of some techniques (all in C#)

Scheduling: Explicit Threading
1. Join
2. IsBackground
3. Interrupt
- only occurs on ThreadWaitSleepJoinState
- dangerous because developers don't expect exceptions coming from wait state
- doesn't interrupt SpinWait
4. Abort
- more dangerous... just plain stops it
- the ThreadAbortException is recognized by the .NET framework as something that shouldn't tear down the application
- ThreadAbortException rethrows the exception (unless a privileged opertation ResetThreadAbort is called)
- abort can happen at any time... between any two operations
5. Suspend/Resume
- causes problems due to contention; the suspended thread may have a lock
- can be used for synchronization (but shouldn't)

Threads are expensive for fine-grained tasks
Sample: spinning up 1000 threads that do nothing takes about 2ms per thread (~4 million cycles per thread) when running with the debugger; about 10x the speed without the debugger

- executes with a pool of threads (QueueUserWorkItem method enqueues)
- manages queues of work items and shared threads
- best for fine-grained tasks
- ThreadPool spins up the threads to begin and they are reused
- runs about 1000 faster with ThreadPool than with straight threads
- under the covers, there are two pools
1. one for work items
2. one for I/O completions (harder to get to)
- min/max threads; default = 0, max = 250 * CPU
- max used to be the cause of frequent deadlocks (fixed in 3.5)
- thread injection and retirement is automatic
- CLR has a daemon thread that watches for blocking
- throttles creation at 2/sec
- min is often used to reduce "startup" latency

Asynchronous Program Model (APM)
AsyncAPI: requires AsyncCallback, and state as parameters; BeginFoo and EndFoo
- BeginFoo schedules  work to run asynchronously
- End is used to return value and throws exceptions
- when you call Begin, you always need to call End
Rendezvous options
1. Blocking: wait on IAsyncResult.AsyncWaitHandle
2. Callback: pass a callback to "Begin"; runs when completed
3. Polling: check IAynscResult.Completed
ThreadPool is still faster than async delegate invocation (async delegate invocation is through remote stack, then gets to the ThreadPool anyhow)

Scheduling: I/O completion port
I/O completion port
- meant for async I/O
- bound to file handles and network sockets
- async I/O is entirely async (in hardware)
- interrupt posts packet to port when complete
- threads block on port to be notified of packets
- Windows throttles waking for good scaling
- UnsafeQueueNativeOverlapped
CLR ThreadPool has a single, global port
- all async I/O via APM goes through it

[Note: File.OpenRead is a synchronous call by default, but there is an overload that takes a Boolean async parameter]

Background Work and UIs (UI marshalling)
- .NET UI controls have thread affinity; controls must be accessed by creating thread)
- each framework has some marshalling mechanism
- Windows Forms: Control.Invoke/BeginInvoke
- WPF: Dispatcher.Invoke/BeginInvoke
- Control.BeginInvoke puts the delegate/work into the correct message queue; not currently necessary to call EndInvoke

- hides marshalling details
- Send (sync) and Post (async) methods
- Send: executes the delegate immediately
- Post: puts the delegate into the ThreadPool
- WindowsFormsSynchronizationContext
- DispatcherSynchronizationContext
- SynchronizationContext.Current
- AsyncOperationManager.SynchronizationContext

- performs work on the right thread
- heavy lifting on ThreadPool thread
- DoWork event
- Progress reporting and completion on the UI thread
- kicked off by a call to RunAsync
- also supports cancellation
- initiated by CancelAsync
- DoWork needs to poll CancelationPending flag
- built on top of SynchronizationContext
- works for both Windows Forms and WPF

Thread Hopping (Execution Context)
- ambient state associated with each thread
- security info, call context
- async points represents a logical continuation
- ExecutionContext
- Capture: gets the current context
- Run: executes a delegate with a captured context
- Flowed automatically
- Thread, ThreadPool, Control.BeginInvoke, etc
- but if you do somthing funky....
- Flow can be suppressed
- SuppressFlow
- ThreadPool.UnsafeQueueUserWorkItem
Posted on Sunday, October 26, 2008 5:12 PM Software Development , PDC 2008 , VB | Back to top

Comments on this post: Concurrent, Multi-Core Programming on Windows and .NET (Part II -- Threading: Stephen Toub)

No comments posted yet.
Your comment:
 (will show your gravatar)

Copyright © Jeff Certain | Powered by: