The major change from Oz 1 to Oz 2 is that Oz 2 is based on sequential composition while Oz 1 is based on concurrent composition. Oz1 creates new threads automatically when needed. Oz 2 creates new threads only when this is explicitly requested with the statement
thread Statement end
Sequential composition and explicit thread creation lead to a
different concurrent programming style. To port a concurrent Oz 1
program to Oz 2, it will be necessary to insert
thread
... end
at the
right places. To do this, you will have to understand the concurrent
structure of your program.
We changed to sequential composition because it makes programming and debugging substantially easier.
Since Oz 2 has sequential composition, it also has first-class locks to synchronize data structures that are accessed by more than one thread.
The change to sequential composition led to a redesign of the object system. While in Oz 1 objects are always synchronized, they must be synchronized explicitly with locks in Oz 2. Sequential object-oriented programming in Oz 2 is easier than in Oz 1 since it is impossible to create a deadlock. Concurrent object-oriented programming in Oz 2 requires the explicit use of locks. Locks provide more flexibility than the built-in object synchronization of Oz 1.
That thread creation is always explicit in Oz 2 (i.e., caused by
thread
... end
) is
not completely true. There are two simple exceptions:
thread
...
end
. Thus statements fed through the programming
interface run concurrently.
We have changed our terminology. In Oz 2 we call a statement what is called an expression in Oz 1, and we call an expression what is called a term in Oz 1.
Oz 1 | Oz 2 | |
---|---|---|
expression | becomes | statement |
term | becomes | expression |
Let's discuss the difference between Oz 1 and Oz 2 with an example. Consider the statement
local X=2 Y=3 Z U in U = Z*Z Z = X+Y {Browse Z} end
In Oz 1 this statement will display 25 in the browser. It will do
so by creating a new thread for the statement U =
Z*Z
.
In Oz 2 this statement will not display anything. The executing
thread will simply block for ever at the statement U =
Z*Z
because Z
is not determined.
To make the statement work in Oz 2, we can rewrite it as follows:
local X=2 Y=3 Z U in thread U = Z*Z end Z = X+Y {Browse Z} end
Oz 1's reduction strategy cannot be expressed in Oz 2. The reason is that
thread Statement end
will create a new thread no matter whether it is needed or not. It turns out that the automatic thread creation of Oz 1 is not needed.
There is no direct translation of Oz 2 programs into Oz 1 programs. One can try to simulate sequential composition with data flow synchronization, but this may require many auxiliary variables and nonlocal program transformations. For instance, procedures will need an extra argument so that they can signal that they have done their job completely.
Here is another example for rewriting an Oz 1 program into an Oz 2 program:
fun {Map Xs F} case Xs of nil then nil [] X|Xr then {F X} | {Map Xr F} end end
In Oz 1, Map
is automatically concurrent. For
instance,
{Browse {Map [1 2 _ 3 4] fun {$ X} X+1 end}}
will show [2 3 _ 4 5]
in the browser. In Oz 2 you will
see nothing since Map will block on the undetermined element of the
list and hence Browse will not be applied. However, it is easy to
write a concurrent Map
in Oz 2:
fun {Map Xs F} case Xs of nil then nil [] X|Xr then thread {F X} end | {Map Xr F} end end