An Intro to Programming with C# ThreadsPresentation by:Jason Bender, Garrett Lund, Ben Gamble, Michael Calvo, and Jeff Corbell
Outline• Introduction
▫ The Basics▫ Why use Concurrency▫ The Design of a Thread Facility
• Using Locks: Accessing Shared Data• Using Wait and Pulse: Scheduling Shared Resources• Using Threads: Working in Parallel• Using Interrupt: Diverting the Flow of Control
Threads: The Basics•What is a thread?
▫Threads allow you to write programs with simultaneous points of execution, synchronizing through shared memory.
•Threads are lightweight▫Because thread creation, existence,
destruction, and synchronization primitives are cheap, programmers will use them for all their concurrency needs.
Single-threaded vs. Multithreaded
Why use concurrency?•Use of multiprocessors•Driving slow devices
▫Disks, networks, terminals, printers•Human users need concurrency•Distributed systems•Reduce Latency
The Design of a Thread Facility•Four Major Mechanisms
1. Thread Creation2. Mutual Exclusion3. Waiting for Events4. Getting out of unwanted long-term wait
Thread Creation•In C# you create a thread by:
1. Creating an object of type “Thread”2. Giving its constructor a “ThreadStart” delegate3. Calling the thread’s “Start” method
•Once Run method is called:1. Starts executing asynchronously with invocation of delegate's method2. Method returns3. Thread dies
Code ExampleThread t = new Thread(new ThreadStart(foo.A)); t.Start();foo.B();t.Join();
Using Locks
Mutual Exclusion•Used to avoid errors with multiple
threads accessing shared variables•Using locks is the simplest tool to
accomplish this•You must only access data from a thread
that is holding that lock
Using Locks (in C#)•General Form:
lock(expression){ embedded-statement(s) }
Locking instance fields of an objectclass KV {
string k, v; public void SetKV(string newk, string newv) { lock (this)
{ this.k = newk; this.v = newv;
} }
}
Locking Objects by Typestatic KV head = null; KV next = null; public void AddToList() { lock (typeof(KV)) { this.next = head;
head = this; }{
Deadlocks involving only locks•In some systems your program will
deadlock if a thread tries to lock a locked object
•C# and Java allow an object to be locked multiple times by the same thread
•The object remains locked until the object is unlocked the same number of times
Deadlocks involving only locks•Simplest case:thread A locks object M1; thread B locks object M2; thread A blocks trying to lock M2; thread B blocks trying to lock M1.•Simple solution: lock objects in the same
order. Make it so all threads do not try to lock M2 until you have obtained the lock to M1
Poor Performance due to locks•The simple solution is not always the best
one•If threads A and B operate on separate
subsets of the data, locking the whole object would decrease performance
Lock Granularity•Take for example a class that manages a
bunch of open buffered files, you should not lock all of the files if you want to write to just one of them
•Solution: lock granularity, only lock what you need to▫Drawback – Locking becomes more difficult
and you might get confused
Using Wait And Pulse
Wait and Pulse•Allows for scheduling multiple threads to
share a common resource.▫Used when mutual exclusion and locks is not
enough▫Called from within a lock statement
Examplereadonly object key = new object();
// thread A lock ( key ) Monitor.Wait( key );
// thread B lock ( key ) Monitor.Pulse( key );
Using “PulseAll”•“PulseAll” awakens all threads that have
called “Wait”.▫Trades slightly poorer performance for greater
simplicity▫Two main reasons to use “PulseAll”
Spurious Wake-ups•Awakening threads that cannot make
useful progress▫Happens when the use of “Wait” is kept simple▫Happens when “PulseAll” is used when “Pulse”
would have been sufficient▫Happens when multiple threads “Wait” on a
single object for multiple reasons.
Spurious Lock Conflicts•A thread is awakened from “Waiting” on
an object, and before doing useful work the thread blocks trying to lock an object.
•C# avoids this problem in simple cases: calling “Monitor.Pulse” which doesn’t actually let the awakened thread start executing. Instead, it is transferred to a “ready queue” on the object.
Starvation•When a program making scheduling
decisions does not allow a thread to run.▫The thread will never make progress
▫Ex. Thread A calls “AcquireShared”; i := 1; Thread B calls “AcquireShared”; i := 2; Thread A calls “ReleaseShared”; i :=
1; Thread C calls “AcquireShared”; i := 2; Thread B calls “ReleaseShared”; i :=
1; … etc.
Deadlocks•Deadlocks can be introduced by waiting on
objects, even though you have been careful to have a partial order on acquiring locks.
▫ Ex.Thread A acquires resource (1);
Thread B acquires resource (2); Thread A wants (2), so it calls “Monitor.Wait” to wait for (2); Thread B wants (1), so it calls “Monitor.Wait” to wait for (1).
Using Threads
Using Threads: Working in Parallel Different situations to split a thread
► Using a multi-processor► Multitasking► Allowing access to multiple clients
Using Threads in User Interfaces If a program is processing , UI should
respond.► Issues:
Longest delay in the system Keeping user input relevant
Using Threads in Network Servers Threads allow a server to assist multiple
clients.► Not everyone is on the same page.
RPC-based systems create new threads with every concurrent call.
Other systems only make a new thread for every connection.
► Less clutter.
Using Threads in Deferring Work Users don't want to wait. Solutions
► Simplest: System returns to caller with result to a method. New thread handles remaining work.
► Better: Same as before, but a single thread handles remaining work for all processes.
► Best: Clean-up thread doesn't need input from any of the main threads. Merges similar requests into the same action.
Using Threads in Pipelining “Many hands make a burden light.”
► Assembly line of data.
Benefit: Makes the most out of multi-processors
Issues:► Balancing equal work amongst all threads.► Knowing how many steps you can use.
Impact of Programming Environs Some factors to take into account when
deciding to use threads.► Capabilities of calling a method: Static vs
Instance Some instance methods require a lock. Some languages provide a syncronization wrapper
around an object instead of needing a lock.
► System cost Significantly more threads than processors results
in slowdown due to the stress of constant rescheduling.
Using Interrupt
Interrupts•Definition:
▫stop a thread and return control to higher level
▫usually to the level that called the interrupt
•Uses: ▫run competing threads and end after one
finishes▫an algorithm takes too long and offer user
cancel option
Interruptsclass PipelinedRasterizer: IDisposable {public void Dispose(){lock(this){if(t1 != null) t1.Interrupt();if(t2 != null) t2.Interrupt();t1 = null; t2 = null;
}}
}
Interrupts•calling interrupt requires thread to be in
wait, sleep, or join state• in C# have thread call Thread.Sleep(0)
occasionally•earlier designs of Java and Modula
included easy ways to do this
Interrupts•Cautions
▫can make code less structured▫harder to debug sections of code
•Exercise restraint▫use interrupts rarely▫only use with the abstraction that
created the thread
Interrupts•Most useful when you don't really know
what's going on▫ex. you don't know where a thread could be
blocked•Don't confuse with exceptions and abort
Questions?