var n: size of buffer buffer: array[ 0 .. n-1 ] of item counterAlgorithm
var: put; where to put next item
- repeat
- .
- produce item
- .
- while counter = n do;
- buffer[ put ] := item;
- put := put + 1 mod n;
- counter := counter + 1
- until false
var get; where to get next item
- repeat
- while counter = 0 do;
- item := buffer[ get ];
- counter := counter - 1;
- .
- consume item
- .
- until false
How to run concurrently? Problem is shared variables which can be changed. On most machines, the lines "counter := counter +/- 1" will be implemented as
Imagine 2 processes executing sequentially:
producer A: consumer B:
. .
. .
. .
fetch (1) fetch (2)
inc (5) inc (3)
store (6) store (4)
. .
. .
. .
Assume the instruction are executed in the order given in parentheses (this would happen if, for example process A timed out after executing the fetch, then process B ran, then process A runs again.
The critical section problem is to provide a mechanism with the following properties:
Assume each process is exectuing at nonzero speed, though no assumption is made about relative speeds.
Performance consideration often require that process's time in a critical section will be as brief as possible because of the possibility of blocking other processes.
Assume processes are of form:
Will look at various methods for entry and exit.
Any real solution will require some type of hardware support. Often the code in a critical section is more complex than a fetch, inc. and store sequence. However, if the task is as simple as protecting a shared variable, this can be done easily (often by turning off the timing interrupt so that the process cannot be interrupted while it is changing the shared variable). A compiler can generate code to support this. Below we assume that if we delcare a variable to be shared, the compiler can generate code to make sure that it is changed without being stopped.
Consider first the 2 process version (that is, only 2 processes share cri. sect)
Solution 1 (poor)
shared: turn
repeat forever
.
.
while turn <> i do;
cri. sect.
turn := j (this in effect exits cri. sect.)
.
.
end.
Problem: this solution flip/flops. If Pi need to enter critical section 10 times and P2 only once (over a period of time), P1 goes once, then must wait for P2 to finally get around to entering (& change shared variable turn) before it can enter again.
Solution 2 (wrong!!!!!!)
shared: flag
repeat forever
.
.
flag[i] := true {i.e. proc i wants to enter cri. sect }
while flag[j] do;
.
.
cri.sect.
.
.
flag[i] := false;
.
.
end
Problem: can get into state where each is waiting for other to enter ( `you go first.' `no, you go first.' `no, I insist.' `no, ...')
Solution 3 (only works with 2 procs)
shared: flag, turn
repeat forever
.
.
flag[i] := true
turn := j
while flag[j] and turn = j do;
.
.
cri. sect.
.
.
flag[i] := false
.
.
end.
Solution 4 Bakery algorithm (for more than 2 processes)
repeat forever
.
.
choosing[i] := true
num[i] := max( num[0], num[1], ... , num[n-1]) + 1
choosing[i] := false
for j := 0 to n-1 do begin
while choosing[j] do;
while (num[j] <> 0) and (num[j],j)<(num[i],i) do;
end;
.
.
critical section
.
.
num[i] := 0
.
.
end
| Index | Previous | Next |
|---|
Copyright ©2005, G. Hill Price
Send comments to G. Hill Price