| CS 330 | Home | Schedule | Resources |
Estimated time: 3 hours
Objective: familiarize yourself with using threads in
programs. First we will study how to use threads with dataflow variables.
Then we will examine observable nondeterminism.
Threads. (3 points)
Consider the two following program fragments:
declare X Y Z in thread if X==1 then Y=2 else Z=2 end end thread if Y==1 then X=1 else Z=2 end end
declare X Y Z in thread if X==1 then Y=2 else Z=2 end end thread if Y==1 then X=1 else Z=2 end end X=2Indicate the values of the variables
X, Y, and Z at
the end of execution. (You may check your guess using the Browser.)
proc {Wait X}
if X==unit then skip else skip end
end
Use your understanding of the == primitive and the semantics of the if statement in your explanation.
declare A B C D in
thread D=C+1 end
thread C=B+1 end
thread A=1 end
thread B=A+1 end
{Browse D}
In what order are the threads created? In what order are the additions done? What is the output? How many possible orders of execution are there? Also, compare with the following:
declare A B C D in
A=1
B=A+1
C= B+1
D=C+1
{Browse D}
Here there is only one thead. In what order are the additions done? What is the output? How many possible execution orders are there? What difference, if any, is there between these two code fragments?
declare
fun {CTest}
C={NewCell 0}
Idone Jdone in
thread % thread T1
for N in 1..10000 do % increment C 10000 times
C:= @C + 1
Idone=true
end
end
thread % thread T2
for N in 1..10000 do % increment C 10000 times
C:= @C + 1
Jdone=true
end
end
{Wait Idone}{Wait Jdone}
@C
end
for X in 1..10 do % run CTest 10 times and display the result
Res = {CTest} in
{Show [' Result is ' Res]}
end
{Show done}
local B in
thread B=true end % (1)
thread B=false end % (2)
if B then % (3)
{Browse yes} % (4)
end
end
For this exercise, do the following:
fun {Fib X}
if X=<2 then 1
else {Fib X-1} + {Fib X-2} end
end
and compare it with the concurrent definition given in section 4.2.3. Run both on the Mozart system to compare their performance.
{Filter In F}, which returns the elements of In for which the boolean function F returns true. Here is a possible definition of Filter:
fun{Filter In F}
case In
of X|In2 then
if{F X} then X|{Filter In2 F}
else {Filter In2 F} end
else
nil
end
end
Executing the following:
{Show {Filter [5 4 1 4 0] fun{$ X} X > 2 end}}
displays
[5 4](Note: unlike Browse, Show outputs to the emulator window and does not update itself if an unbound variable becomes bound.) So Filter works as expected in the case of a sequential execution when all the input values are available. Let us now explore the dataflow behavior of Filter.
declare A
{Show {Filter [5 1 A 4 0] fun {$ X} X>2 end}}
One of the list elements is a variable A that is not yet bound to a value. Remember that the case and if statements will suspend the thread in which they execute, until they can decide which alternative path to take.
declare Out A
thread Out={Filter [5 1 A 4 0] fun{$ X} X>2 end} end
{Show Out}
Remember that calling Show displays its argument as it exists at the instant of the call. Several possible results can be displayed. Which, and why? Is the Filter function deterministic? Why or why not?
declare Out A
thread Out={Filter [5 1 A 4 0] fun {$ X} X>2 end} end
thread A=6 end
{Delay 1000}
{Show Out}
What is displayed and why? What effect does the call to Delay have on the possible outputs?fun lazy {Three} {Delay 1000} 3 end
Calculating {Three} + 0 returns 3 after a 1000 ms delay. This is as expected, since the addition needs the result of {Three}. Now calculate {Three}+0 three times in succession. Each calculation waits 1000 ms. "But wait!" cries Bob. "Three is supposed to be lazy, so its result should be calculated only once!" Explain to Bob why this happens.
declare
fun lazy {MakeX} {Browse x} {Delay 3000} 1 end
fun lazy {MakeY} {Browse y} {Delay 6000} 2 end
fun lazy {MakeZ} {Browse z} {Delay 9000} 3 end
X={MakeX}
Y={MakeY}
Z={MakeZ}
{Browse (X+Y)+Z}
This displays x and y immediately, z after six seconds, and the result 6 after fifteen seconds. Explain this behavior. What happens if (X+Y)+Z is replaced by X+(Y+Z) or by thread X+Y end + Z? Which form gives the final result the quickest? How would you program the addition of n integers i1,...,in, given that integer ij only appears after Tj ms, so that the final result appears the quickest?