400 likes | 541 Views
An Intro to Programming with C# Threads. Presentation 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
E N D
An Intro to Programming with C# Threads Presentation 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.
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 • Thread Creation • Mutual Exclusion • Waiting for Events • Getting out of unwanted long-term wait
Thread Creation • In C# you create a thread by: • Creating an object of type “Thread” • Giving its constructor a “ThreadStart” delegate • Calling the thread’s “Start” method • Once Run method is called: 1. Starts executing asynchronously with invocation of delegate's method 2. Method returns 3. Thread dies
Code Example Thread t = new Thread(newThreadStart(foo.A)); t.Start(); foo.B(); t.Join();
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 object class KV { string k, v; public void SetKV(string newk, string newv) { lock (this) { this.k = newk; this.v = newv; } } }
Locking Objects by Type static 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
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
Example readonly 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: 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.
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
Interrupts class 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