“居眠り床屋”を Squeak Smalltalk で


http://twitter.com/yukihiro_matz/statuses/4079331380 経由で http://blog.bestinclass.dk/index.php/2009/09/scala-vs-clojure-round-2-concurrency/ より。

The problem

An old concurrency problem/exercise is ‘The sleeping barber’ and you can either read the full definition here or read my shorter version here:

We simulate a barber-shop with 1 barber, 3 waiting chairs and 1 chair for haircutting.

The barber sleeps between customers and is thus awoken when one enters the shop. If customers come in while he’s working they take a seat, if no seats are available they leave.


STM とか actor とか格好いいのではないですが、オーソドックスに SharedQueue を使って。

| queue seats debug shop barber watcher done |
queue := SharedQueue new.
done := SharedQueue new.
seats := 3.
debug := SharedQueue new.

shop := [:a |
  debug nextPut: {'(s) entering shop'. a}.
  queue size < seats
    ifTrue: [queue nextPut: a]
    ifFalse: [debug nextPut: {'(s) turning away customer'. a}]].

barber := [
  | next |
  (100 + 600 atRandom) milliSeconds asDelay wait.
  next := done nextPut: queue next.
  debug nextPut: {'(b) cutting hair of customer'. next}].

watcher := [[barber value] repeat] fork.

1 to: 20 do: [:customer |
  (100 + 200 atRandom) milliSeconds asDelay wait.
  [shop value: customer] fixTemps fork].

2 seconds asDelay wait.
watcher terminate.
World findATranscript: nil.
debug size timesRepeat: [
  | next |
  next := debug next.
  Transcript cr; show: (next first forceTo: 31 paddingWith: $ ), next second printString].
Transcript cr; show: '(!) ', done size printString, ' customers got haircuts today'
(s) entering shop              1
(b) cutting hair of customer   1
(s) entering shop              2
(s) entering shop              3
(b) cutting hair of customer   2
(s) entering shop              4
(s) entering shop              5
(s) entering shop              6
(s) turning away customer      6
(b) cutting hair of customer   3
(s) entering shop              7
(b) cutting hair of customer   4
(s) entering shop              8
(s) entering shop              9
(s) turning away customer      9
(s) entering shop              10
(s) turning away customer      10
(b) cutting hair of customer   5
(s) entering shop              11
(b) cutting hair of customer   7
(s) entering shop              12
(s) entering shop              13
(s) turning away customer      13
(b) cutting hair of customer   8
(s) entering shop              14
(s) entering shop              15
(s) turning away customer      15
(b) cutting hair of customer   11
(s) entering shop              16
(s) entering shop              17
(s) turning away customer      17
(s) entering shop              18
(s) turning away customer      18
(b) cutting hair of customer   12
(s) entering shop              19
(b) cutting hair of customer   14
(s) entering shop              20
(b) cutting hair of customer   16
(b) cutting hair of customer   19
(b) cutting hair of customer   20
(!) 13 customers got haircuts today


Smalltalk でもこうして 30行程度で書けるので ClojureScala よりコンパクトに書けているのはS式だからと言うことでもないような…。


あー。SharedQueue だと、シートの空きを確認して座ろうとしたらもう他の誰かが座っていた的なことが起こりかねないのでやっぱりモニター版で書き直し。行数はさほど変わらない。

| queue seats debug shop barber watcher monitor tally |
queue := OrderedCollection new.
tally := 0.
monitor := Monitor new.
seats := 3.
debug := OrderedCollection new.

shop := [:a |
  monitor critical: [
    debug add: {'(s) entering shop'. a}.
    queue size < seats
      ifTrue: [queue add: a]
      ifFalse: [debug add: {'(s) turning away customer'. a}]]].

barber := [
  | next |
  (100 + 600 atRandom) milliSeconds asDelay wait.
  monitor critical: [
    queue notEmpty ifTrue: [
      next := queue removeFirst.
      tally := tally + 1.
      debug add: {'(b) cutting hair of customer'. next}]]].

watcher := [[barber value] repeat] fork.

1 to: 20 do: [:customer |
  (100 + 200 atRandom) milliSeconds asDelay wait.
  [shop value: customer] copy fixTemps fork].

2 seconds asDelay wait.
watcher terminate.
World findATranscript: nil.
debug do: [:each |
  Transcript cr; show: (each first forceTo: 31 paddingWith: $ ), each second printString].
Transcript cr; show: '(!) ', tally printString, ' customers got haircuts today'