You are on page 1of 43

Modern Concurrency

Abstractions for C#

presented by: Guy Gueta

Advanced Software Tools Seminar


December, 2004
Agenda
Concurrency
Concurrency and Languages
Asynchronous Programming
C# and .NET
Polyphonic C#
Examples
Performance
Concurrency
Concurrency is widely used in modern
code
It is very difficult to write/debug concurrent
programs
Concurrency
Can affect the ability to invoke libraries
strtok(….) / Rand() & SRand(….)
Can significantly affect the meaning of virtually
every construct in the language
X++ ;
X=Y;
Most popular programming languages treat
concurrency not as a language feature, but as a
collection of external libraries
Language Features / Libraries
Many features can be provided either as
language features or as libraries
– Memory management
– Exceptions
– Locks
The compiler can analyze language features
– Can produce better code (e.g. local new)
– Can warn programmers of potential and actual
problems.
Polyphonic C#
Example: Monitor
The concept of monitors [Hoare 1974]
The general notion of monitors has
become very popular
public synchronized void Add(int v)
{
// body
}

public synchronized int Get(int v)


{
// body
}

wait()/notify()
Asynchronous Programming
Asynchronous events and message
passing are increasingly used at all
levels of software systems
e.g. Distributed Applications/components,
GUI, Device Drivers
Many asynchronous messages should be
handled concurrently. Many threads are
used to handle them
Polyphonic C#
C# and .NET
Modern, type-safe, object-oriented
programming language
Very similar to Java
C# programs run on top of the .NET
Framework, which includes a multi-
language execution engine and a rich
collection of class libraries.
– .NET IL
JAVA C#

public class Point { public class Point {


protected int x, y; protected int x, y;

public Point() public Point()


{ setPoint( 0, 0 ); } { setPoint( 0, 0 ); }

public Point( int a, int b ) public Point( int a, int b )


{ setPoint( a, b ); } { setPoint( a, b ); }

public void setPoint( int a, int b ) public void setPoint( int a, int b )
{ {
x = a; x = a;
y = b; y = b;
} }

public int getX() { return x; } public int getX() { return x; }


public int getY() { return y; } public int getY() { return y; }
} }
Concurrency in .NET
Threads
Sync. Event (Semaphore, Mutex)
Reader/writer lock
Asynchronous programming model based
on delegates
lock (C#)
Polyphonic C#

Adds just two new concepts: asynchronous


methods and chords
Synchronous Methods
Regular C# methods
The caller makes no progress until the
callee completes
Asynchronous Method
Any call to it is guaranteed to complete
essentially immediately
Never returns a result (or throws an exception)
calling it is much like sending a message, or
posting an event
declared by using the async keyword instead of
void
public async postEvent(EventInfo data)
{
// large method body
}

Usually defined using chords –


do not necessarily require new threads
Chord
A set of method declarations separated
by ‘&’ And a body.

public string Foo1() & public async Foo2(string s1) &


public async Foo3(string s2) & private async Foo4(string s3)
{
// Body
}

At most one method may be synchronous.


Return Value type = Synchronous method type
Chord
public async Foo1() & public async Foo2(string s1) &
public async Foo3(string s2) & private async Foo4(string s3)
{
…..
}

public string Foo1() & public async Foo2(string s1) &


public async Foo3(string s2) & private async Foo4(string s3)
{
…..

return myString;
}
Trivial Chord = Regular method
public class Buffer
{
public string Get() & public async Put(string s) { return s; }
}
Chord
Thread 1 Thread 2
Put(“1”)
b.Put(“1”); Writeln(b.Get()); Get()
Writeln(b.Get()); b.Put(“2”); Put(“3”)
Writeln(b.Get()); b.Put(“3”);
b.Put(“4”); Writeln(b.Get());

OUTPUT:
1
2
3
4
Buffer buff = new Buffer();
buff.Put(“blue”);
buff.Put(“sky”);
Console.Write(buff.Get());
Console.Write(buff.Get());

bluesky
skyblue

[In a real implementation the nondeterminism may be resolved]


Thread safety
The chord mechanism is thread safe
– The locking that is required is generated automatically
by the compiler
– deciding whether any chord is enabled by a call and,
if so, removing the other pending calls from the
queues and scheduling the body for execution, is an
atomic operation.
no monitor-like mutual exclusion between chord
bodies
Any mutual exclusion that is required must be
explicitly programmed in terms of
synchronization conditions in chord headers
A Simple Cell Class
public class OneCell
{
public OneCell()
{
empty();
}
public void Put(object o) & private async empty()
{
contains(o);
}
public object Get() & private async contains(object o)
{
empty();
return o;
}
}
One method in multiple chords
It is possible (and common) to have multiple
chords involving a given method

public class Buffer


{
public string Get() & public async Put(string s)
{
return s;
}
public string Get() & public async Put(int n)
{
return n.ToString();
}
}
nondeterminism
Within a single method-header
public void AddOne(ref int a) public void Random(out int a)
{ {
a++; a = ….
} }

Int x = 7; Int a ;
AddOne(ref x); Random(out a);

If return-type is async then the formal


parameter list formals may not contain any
ref or out parameter modifier
Within a single chord-declaration
At most one method-header may have a
non-async return-type
If the chord has a method-header with
return-type type, then body may use
return statements with type expressions,
otherwise body may use empty return
statements
All the formals appearing in method-
headers must have distinct identifiers
Within a single chord-declaration –
cont’
Two method-headers may not have both
the same member-name and the same
argument type signature
The method-headers must either all
declare instance methods or all declare
static methods
Within a particular class
All method-headers with the same
member-name and argument type
signature must have the same return-type
and identical sets of modifiers

public string Get() & public async Put(string s)


{
return s;
}
private int Get() & public async Put(int n)
{
return n.ToString();
}
struct/class
struct ST class CL
{ {
public int x ; public int x ;
} }

public void foo() public void foo()


{ {
ST s1,s2 ; CL c1,c2 ;
s1.x = 7 ; c1 = new CL() ;
s2 = s1 ; c1.x = 7 ;
s1.x = 18 ; c2 = c1 ;
…. c1.x = 18 ;
} ….
}
Within a particular class – cont’
If it is a value class (struct), then only
static methods may appear in non-trivial
chords
virtual-override
Class A Class B : A
{ {
public void foo1() public override void foo2()
{ {
….
….
}
}
}

public virtual void foo2()


{
….
}
}
class C
{
public virtual void f () & public virtual async g() { /∗ body1 ∗/ }

public virtual void f () & public virtual async h() { /∗ body2 ∗/ }


}

class D : C
{
public override async g() { /∗ body3 ∗/ }
}

class E
{
public virtual void f() & private async g() { /∗ body4 ∗/ }
}
Within a particular class – cont’
If any chord-declaration includes a virtual
method m with the override modifier, then
any method n that appears in a chord with
m in the super class containing the
overridden definition of m must also be
overridden in the subclass
RendezVous
class RendezVous
{
private class Thunk
{
int wait() & async reply(int j ) {return j ;}
}
public int f (int i)
{
Thunk t = new Thunk();
af (i, t);
return t.wait();
}
private async af (int i, Thunk t) & public int g(int j )
{
t . reply( j ); // returning to f
return i; // returning to g
}
}
ReaderWriter 1
class ReaderWriter
{
ReaderWriter() {idle();}

public void Shared() & async idle() {s(1); }


public void Shared() & async s(int n) {s(n + 1); }
public void ReleaseShared() & async s(int n)
{
if (n == 1) idle(); else s(n − 1);
}
public void Exclusive() & async idle() {}
public void ReleaseExclusive() { idle(); }
}
ReaderWriter 2
class ReaderWriter
{
ReaderWriter() { idle(); }
private int n = 0; // protected by s()

public void Shared() & async idle() { n = 1; s(); }


public void Shared() & async s() { n++; s(); }
public void ReleaseShared() & async s()
{
if (−−n == 0) idle(); else s();
}
public void Exclusive() & async idle() {}
public void ReleaseExclusive() { idle(); }
}
class ReaderWriterFair
{
ReaderWriter() { idle(); }
private int n = 0; // protected by s()

public void Shared() & async idle() { n = 1; s(); }


public void Shared() & async s() { n++; s(); }
public void ReleaseShared() & async s()
{
if (−−n == 0) idle(); else s();
}
public void Exclusive() & async idle() {}
public void ReleaseExclusive() { idle(); }

public void ReleaseShared() & async t()


{
if (−−n == 0) idleExclusive(); else t();
}
public void Exclusive() & async s() { t(); wait(); }
void wait() & async idleExclusive() {}
}
Delegate

delegate bool MyDlg(string s,int I);

MyDlg d = new MyDlg(MyObj.f);


d(“Hello”, 7);
async & void

async is a subtype of void


– A void delegate may be created from an
async method
– An async method may override a void one
– An async method may implement a void
method in an interface
Combining Asynchronous
Messages

public delegate async IntCallback(int result);

public class Service


{
public async Request(string arg, IntCallback cb)
{
int r ;
. . . // do some work
cb(r); // send the result back
}
}
class Client {
public static void Main(string[] args) {
Service s1 = . . . ;
Service s2 = . . . ;
Join2 x = new Join2();
s1.Request(args[0], x . firstcb);
s2.Request(args[1], x . secondcb);
. . . // do something useful in the meantime. . .
int i, j ;
x.wait(out i, out j ); // wait for both results to come back
. . . // do something with them
}
}
class Join2
{
public IntCallback firstcb;
public IntCallback secondcb;

public Join2()
{
firstcb = new IntCallback(first);
secondcb = new IntCallback(second);
}

public void wait(out int i, out int j ) & async first(int fst)
& async second(int snd)
{
i = fst; j = snd;
}
}
Active Objects
public abstract class ActiveObject {

protected bool done;

abstract protected void ProcessMessage();

public ActiveObject () {
done = false;
mainLoop();
}

async mainLoop() {
while (!done) ProcessMessage();
}
}
public class StockServer : ActiveObject {

public async AddClient(Client c) // add new client


& override protected void ProcessMessage() {
// Body
}

public async CloseDown() // request to terminate


& override protected void ProcessMessage() {
done = true;
}
}
Remarks on Concrete Syntax
class ReaderWriter {
private async idle(); // Just signatures. Any modifiers or
private async s(int); // attributes would occur here too

public ReaderWriter() { idle(); }

public void Shared()


when idle() { s(1); }
when s(int n) { s(n + 1); }

public void ReleaseShared()


when s(int n) { if (n == 1) idle(); else s(n − 1); }

public void Exclusive()


when idle() {}
….
References
Modern Concurrency Abstractions for C#.
NICK BENTON, LUCA CARDELLI, and
CEDRIC FOURNET. Microsoft Research.
Applied Microsoft .NET Framework Programming.
Jeffrey Richter. Microsoft Press.
Java: How to Program, 4th ed. H.M. Deitel, P.J.
Deitel. Prentice Hall, 2002.
Monitors: An operating system structuring
concept. HOARE, C. A. R. 1974. Comm. ACM 17, 10
(Oct.), 549–557.
Thread Synchronization Fairness in the .NET CLR.
Jeffrey Richter. 2003.

You might also like