COMP 150-CCP Concurrent Programming Lecture 21: Concurrency in Other Environments (Part 2) Dr. Richard S. Hall rickhall@cs.tufts.edu Clement Escoffier clement.escoffier@gmail.com Concurrent programming April 10th, 2008
Agenda.NET and C#
What is.net? The.NET Framework is Microsoft's managed code programming model for building applications on Windows clients, servers, and mobile or embedded devices. Developers use.net to build applications of many types: Web applications, server applications, smart client applications, console applications, database applications, and more.
What is.net? VB.NET C# J# Managed C++ Cobol.Net Web Forms, Web Services, ASP.NET Windows Forms.NET Framework Library Common Language Runtime (CLR) Win
What is.net? The CLR is a virtual machine Support several programming languages Interpreting Intermediate Language (MSIL) Close to Java on a lot of points.net provides a lot of library to create Windows GUI Web Applications Windows Vista uses a lot the.net framework
Inside the CLR Base Class Library Exception Manager Thread Support COM Marshaller Type Checker Debug Engine Code Manager JIT Compiler (IL => Native) Garbage Collector Class Loader
In this Lecture How to handle multi-threading in C#? C# is the most famous.net language Very close to Java Between C++ and Java How to create threads How to use monitors How to use condition variables
Using Thread in C# Import the System.Threading namespace using System.Threading; Equivalent to packages in Java
Creating a Thread Two steps: First create a ThreadStart object (delegate) In argument, give the pointer on the function to call ThreadStart ts = new ThreadStart(<function name>); No Runnable interface to implement Then, create the Thread object Thread newthread = new Thread(ts); To Start the Thread, call the start method: newthread.start();
Thread Creation Example using System; using System.Threading; class Test { static void Main() { Work w = new Work(); w.data = 42; ThreadStart threaddelegate = new ThreadStart(w.DoWork); Thread newthread = new Thread(threadDelegate); newthread.start(); class Work { public int Data; public void DoWork() { Console.WriteLine("Instance thread procedure. Data={0", Data);
The Simplest Way (with Argument) public static void Main() { Thread newthread = new Thread(Work.DoWork); newthread.start(42); Work w = new Work(); newthread = new Thread(w.DoWork); // Pass an object containing data for the thread. newthread.start(42); public void DoMoreWork(object data) { Console.WriteLine("Instance thread procedure. Data='{0'", data);
Waiting for Thread Termination Two Join methods are available on the Thread object Join() Blocks the calling thread until a thread terminates Join(Int32) Blocks the calling thread until a thread terminates or the specified time elapses
Waiting for Thread Termination An event base system is also available A thread waits until another thread has send an event Several types of event and wait methods are availble.
Waiting for Thread Termination static void Main() { AutoResetEvent autoevent = new AutoResetEvent(false); Thread regularthread = new Thread(new ThreadStart(ThreadMethod)); regularthread.start(); ThreadPool.QueueUserWorkItem(new WaitCallback(WorkMethod), autoevent); // Wait for foreground thread to end. regularthread.join(); // Wait for background thread to end. autoevent.waitone(); static void ThreadMethod() { Console.WriteLine("ThreadOne, executing ThreadMethod, " + "is {0from the thread pool.", Thread.CurrentThread.IsThreadPoolThread? "" : "not "); static void WorkMethod(object stateinfo) { Console.WriteLine("ThreadTwo, executing WorkMethod, " + "is {0from the thread pool.", Thread.CurrentThread.IsThreadPoolThread? "" : "not "); // Signal that this thread is finished. ((AutoResetEvent)stateInfo).Set();
Suspending/Resuming a Thread As in Java, the Suspend and Resume method are obsolete
Using Monitors C# provides a keyword equivalent to the Java synchronized keyword Lock(object) This keyword can t be use on method signature Only in the method code Lock are re-entrant in C#
Using Monitors int Withdraw(int amount) { if (balance < 0) { throw new Exception("Negative Balance"); lock (this) { if (balance >= amount) { Console.WriteLine("Balance before Withdrawal : " + balance); Console.WriteLine("Amount to Withdraw : -" + amount); balance = balance - amount; Console.WriteLine("Balance after Withdrawal : " + balance); return amount; else { return 0; // transaction rejected
Using Monitors: The Monitor Class Locks are mapped on a static class: the Monitor Class The lock statement is implemented by using the Enter and Exit methods of the Monitor class, and it uses try catch finally to ensure that the lock is released. A Monitor object maintain A reference to the thread that currently holds the lock. A reference to a ready queue, which contains the threads that are ready to obtain the lock. A reference to a waiting queue, which contains the threads that are waiting for notification of a change in the state of the locked object.
Using Monitors: The Monitor Class Enter, TryEnter Acquires a lock for an object. This action also marks the beginning of a critical section. Wait Releases the lock on an object in order to permit other threads to lock and access the object. Pulse, PulseAll Sends a signal to one or more waiting threads. The signal notifies a waiting thread that the state of the locked object has changed, and the owner of the lock is ready to release the lock. Exit Releases the lock on an object. This action also marks the end of a critical section protected by the locked object.
Using Monitors: The Monitor Class int Withdraw(int amount) { if (balance < 0) { throw new Exception("Negative Balance"); Monitor.Enter(this); if (balance >= amount) { Console.WriteLine("Balance before Withdrawal : " + balance); Console.WriteLine("Amount to Withdraw : -" + amount); balance = balance - amount; Console.WriteLine("Balance after Withdrawal : " + balance); return amount; else { return 0; // transaction rejected Monitor.Exit(this); This is not correct!
Using Monitors: The Monitor Class int Withdraw(int amount) { if (balance < 0) { throw new Exception("Negative Balance"); Monitor.Enter(this); if (balance >= amount) { Console.WriteLine("Balance before Withdrawal : " + balance); Console.WriteLine("Amount to Withdraw : -" + amount); balance = balance - amount; Console.WriteLine("Balance after Withdrawal : " + balance); return amount; else { Monitor.Exit(this); return 0; // transaction rejected Monitor.Exit(this); This is correct!
Monitors & Condition Variables The Monitor class specify Different wait method Wait(Object) Releases the lock on an object and blocks the current thread until it reacquires the lock. Wait(Object, Int32) Releases the lock on an object and blocks the current thread until it reacquires the lock. If the specified time-out interval elapses, the thread enters the ready queue. Wait(Object, Int32, Boolean) Releases the lock on an object and blocks the current thread until it reacquires the lock. If the specified time-out interval elapses, the thread enters the ready queue. This method also specifies whether the synchronization domain for the context (if in a synchronized context) is exited before the wait and reacquired afterward.
Monitors & Condition Variables The Monitor class specify Different notify method Pulse Notifies a thread in the waiting queue of a change in the locked object's state. PulseAll Notifies all waiting threads of a change in the object's state.
C# Bounded Buffer class LockedBuffer : Buffer { private Object[] buf; private int count = 0; private int input = 0; private int output = 0; // public LockedBuffer(int size) { buf = new Object[size];
C# Bounded Buffer public void Put(Object o) { lock (this) { while (count == buf.length) { try { Monitor.Wait(this); catch (SynchronizationLockException e) { Console.WriteLine(e); return; catch (ThreadInterruptedException e) { Console.WriteLine(e); return; Console.WriteLine("PUT "); for (int i = 0; i < count; i++) { Console.WriteLine(" " + buf[(i + output) % buf.length] + " "); Console.WriteLine("[" + o + "]"); buf[input] = o; ++count; input = (input + 1) % buf.length; Monitor.PulseAll(this);
C# Bounded Buffer public Object Get() { lock (this) { while (count == 0) { try { Monitor.Wait(this); catch (SynchronizationLockException e) { Console.WriteLine(e); return null; catch (ThreadInterruptedException e) { Console.WriteLine(e); return null; Object o = buf[output]; buf[output] = null; --count; output = (output + 1) % buf.length; Monitor.PulseAll(this); Console.WriteLine("GET [" + o + "] "); for (int i = 0; i < count; i++) { Console.WriteLine(buf[(i + output) % buf.length] + " "); Console.WriteLine(""); return o;
C# Bounded Buffer public class Producer { private Buffer buf; private String alphabet = "abcdefghijklmnopqrstuvwxyz"; private bool paused = true; public Producer(Buffer b) { buf = b; public void Pause() { lock (this) { paused =!paused; Monitor.PulseAll(this); public void Run() { int ai = 0; while (true) { lock (this) { while (paused) { Monitor.Wait(this); buf.put((alphabet.tochararray()[ai])); ai = (ai + 1) % alphabet.length; Thread.Sleep(500);
C# Bounded Buffer public class Consumer { private Buffer buf; private bool paused = true; public Consumer(Buffer b) { buf = b; public void Pause() { lock(this) { paused =!paused; Monitor.PulseAll(this); public void Run() { while (true) { lock (this) { while (paused) { Monitor.Wait(this); Object o = buf.get(); Thread.Sleep(500);
C# Bounded Buffer static void Main(string[] args) { Buffer buffer = new LockedBuffer(5); Producer prod = new Producer(buffer); Consumer cons = new Consumer(buffer); Thread producer = new Thread(new ThreadStart(prod.Run)); Thread consumer = new Thread(new ThreadStart(cons.Run)); producer.start(); consumer.start(); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Gui(prod, cons));
Other Synchronization Mechanisms The Managed Thread Pool Timers Wait Handles EventWaitHandle, AutoResetEvent, and ManualResetEvent Mutexes Interlocked Operations Reader-Writer Locks Semaphores
Conclusion.NET provided synchronization mechanisms very similar to Java It provides almost the same class than the Java Concurrent API These classes are supported on Compact.NET All synchronization classes are usable in any others.net languages Except the lock macro