The Problem with Treads Edward A. Lee Programming Technology Lecture 2 11/09/08
Background on Edward A. Lee Bachelors degree (Yale University) (1979) Master degree (MIT) (1981) Ph.D. (U. C. Berkeley) (1986) Fellow of the IEEE Robert S. Pepper Distinguished Professor Former chair of the Electrical Engineering and Computer Sciences department at U.C. Berkeley Director of Berkeley Center for Hybrid and Embedded Software Systems Director of the Berkeley Ptolemy project He is co-author of five books and numerous papers.
About IEEE Computer The largest society in IEEE Founded i 1946 "Dedicated to advancing the theory and application of computer and information-processing technology" Known worldwide for its computer-standards activities The CS promotes an active exchange of ideas and technological innovation among its members
Intro Concurrent programming is difficult. Concurrent programming is becoming more urgent. The performance gains in computing, must be done by exploiting parallelism Automatic exploitation of parallelism in sequential programs Automatic program parallelization of sequential programs Automatic techniques have been pushed about as far as it will go It capable of exploiting only modest parallelism
Human vs. concurrency Physical world is highly concurrent survival depends on our ability to reason about concurrent physical dynamic Concurrent abstractions Not resemble the concurrency of the physical world Used to these computational abstractions That we think that the can not change The paper argue that the difficulty of concurrent programming is a consequence of the abstractions, the problem will be fixable if let go of those abstractions.
Threads The dominating approach to concurrent programming Sequential processes that share memory Key concurrency model supported by modern computers programming languages operating systems Symmetric multiprocessors are direct hardware realizations of the thread abstraction Can effectively be used by Webserver independence of these applications more like processes than threads where memory is not shared database abstractions However client-side applications are not so simple.
Threads Not the only possibility data parallel language extensions message passing libraries These are often used in scientific computing where threads not are the dominating solution This often differ significantly from so-called general purpose architectures In embedded applications reliability and predictability over expressiveness or performance The paper argues that achieving reliability and predictability using threads is essentially impossible for many applications.
Threads The core abstraction, which all widely-used programming languages are built on, emphasizes deterministic composition of deterministic components Threads are wildly nondeterministic The programmer has the job to prune away that nondeterminism by using Semaphores Monitors The Paper argue that we must (and can) build concurrent models of computation that are far more deterministic, and that we must judiciously and carefully introduce nondeterminism where needed.
public class ValueHolder { private List listeners = new LinkedList(); private int value; public interface Listener { public void valuechanged(int newvalue); public void addlistener(listener listener) { listeners.add(listener); public void setvalue(int newvalue) { value = newvalue; Iterator i = listeners.iterator(); while(i.hasnext()) { ((Listener)i.next()).valueChanged(newValue);
public class ValueHolder { private List listeners = new LinkedList(); private int value; public interface Listener { public void valuechanged(int newvalue); public void addlistener(listener listener) { listeners.add(listener); public void setvalue(int newvalue) { value = newvalue; Iterator i = listeners.iterator(); while(i.hasnext()) { ((Listener)i.next()).valueChanged(newValue);
public class ValueHolder { private List listeners = new LinkedList(); private int value; public interface Listener { public void valuechanged(int newvalue); public void addlistener(listener listener) { listeners.add(listener); public void setvalue(int newvalue) { value = newvalue; Iterator i = listeners.iterator(); while(i.hasnext()) { ((Listener)i.next()).valueChanged(newValue);
public class ValueHolder { private List listeners = new LinkedList(); private int value; public interface Listener { public void valuechanged(int newvalue); public synchronized void addlistener(listener listener) { listeners.add(listener); public void setvalue(int newvalue) { List copyoflisteners; synchronized(this) { value = newvalue; copyoflisteners = new LinkedList(listeners); Iterator i = copyoflisteners.iterator(); while(i.hasnext()) { ((Listener)i.next()).valueChanged(newValue);
public class ValueHolder { private List listeners = new LinkedList(); private int value; public interface Listener { public void valuechanged(int newvalue); public synchronized void addlistener(listener listener) { listeners.add(listener); public void setvalue(int newvalue) { List copyoflisteners; synchronized(this) { value = newvalue; copyoflisteners = new LinkedList(listeners); Iterator i = copyoflisteners.iterator(); while(i.hasnext()) { ((Listener)i.next()).valueChanged(newValue);
public class ValueHolder { private List listeners = new LinkedList(); private int value; public interface Listener { public void valuechanged(int newvalue); public synchronized void addlistener(listener listener) { listeners.add(listener); public void setvalue(int newvalue) { List copyoflisteners; synchronized(this) { value = newvalue; copyoflisteners = new LinkedList(listeners); Iterator i = copyoflisteners.iterator(); while(i.hasnext()) { ((Listener)i.next()).valueChanged(newValue);
public class ValueHolder { private List listeners = new LinkedList(); private int value; public interface Listener { public void valuechanged(int newvalue); public synchronized void addlistener(listener listener) { listeners.add(listener); public void setvalue(int newvalue) { List copyoflisteners; synchronized(this) { value = newvalue; copyoflisteners = new LinkedList(listeners); Iterator i = copyoflisteners.iterator(); while(i.hasnext()) { ((Listener)i.next()).valueChanged(newValue);
public class ValueHolder { private List listeners = new LinkedList(); private int value; public interface Listener { public void valuechanged(int newvalue); public synchronized void addlistener(listener listener) { listeners.add(listener); public void setvalue(int newvalue) { List copyoflisteners; synchronized(this) { value = newvalue; copyoflisteners = new LinkedList(listeners); Iterator i = copyoflisteners.iterator(); while(i.hasnext()) { ((Listener)i.next()).valueChanged(newValue);
Fixing Threads by More Aggressive Pruning Better software engineering processes Ptolemy Project (2000) code maturity rating system design reviews code reviews nightly builds regression tests 100 percent code coverage No problems were observed until the code deadlocked four years later The paper conclude that testing may never reveal all the problems in nontrivial multithreaded code. Quick fix: always acquire locks in the same order method signature do not indicates what locks, it needs
Fixing Threads by More Aggressive Pruning 2 Software engineering process improvements alone will not do the job Use transactions, like in databases well-suited for multiple actors compete nondeterministically for resources not well-suited for building determinate concurrent interactions Concurrency is encapsulated into libraries by experts Using Proxies of the data Introduces formal program analysis to identify potential concurrency bugs in multi threaded programs still require considerable expertise Fix some but not all still result in highly nondeterministic programs
Fixing Threads by More Aggressive Pruning 2 Instead of startingwith a highly nondeterministic mechanism like threads, and relying on the programmer to prune that nondeterminacy, we should start with deterministic, and introduce nondeterminism only where needed
Alternatives to Threads
Alternatives to Threads 2
Alternatives to Threads 3 everything about the program is deterministic except the explicitly nondeterministic interaction specified by the Merge block
Challenges and Opportunities alternatives to threads have been around for a long time the core abstractions of computation, are deeply rooted in the sequential paradigm threads are either a minor extension to these languages java, just an external library. We should not replace established languages. We should instead build on them. The paper believe that the right answer is coordination languages. But coordination languages have also been around for a long time failed because of the homogeneity
Conclusion Concurrency in software is difficult blame ourselves of the abstractions for concurrency that we have chosen The dominant one is Threads We can improve by using design patterns better granularity of atomicity (e.g. transactions) improved languages formal methods Concurrent programming models can be constructed that are much more predictable and understandable than threads Nondeterminism should be introduced where needed, and should be explicit in programs
My Conclusion Threads are generally not bad Too much work in not using them Know what, you are doing, when programming
Reviewer Questions