無限素数列ジェネレーター版をスレッドセーフに

ちなみにGaucheの遅延シーケンスはスレッドセーフで、複数のスレッドが同時に頭から素数を取って行っても安全です。こちらの実装はどうですか? (挑戦状)

shiro - 2011/10/02 12:09


ちょっと調べてみた感じでは、Squeak のジェネレーターでスレッドセーフは考慮されていないようです。残念。

実際、複数のスレッドからアクセスすると #next で素数ではなく変なもの(コンテキスト)が戻ってくることがあり、正しい答えを返せなくなります。

| primesGen queue |
primesGen := Generator on: [:g |
   | primes next |
   primes := OrderedCollection withAll: #(2 3 5 7).
   primes do: [:prime | g value: (next := prime)].
   [  [:exit | [
         next := next + 2.
         primes anySatisfy: [:prime |
            prime * prime > next ifTrue: [exit value].
            next isDivisibleBy: prime]
      ] whileTrue] valueWithExit.
      g value: (primes add: next)
   ] repeat].

queue := SharedQueue2 new.
100 timesRepeat: [
   [50 timesRepeat: [
      queue nextPut: primesGen next -> Processor activeProcess name.
      Processor yield]
   ] fork].
5000 timesRepeat: [result := queue next key].
result   "=> 47533 "


Generator>>#next を排他的にしてしまえば改善するはずなので試してみましょう。

Stream subclass: #Generator
instanceVariableNames: 'block next continue home monitor'

Generator >> initializeOn: aBlock
monitor := Monitor new.
block := aBlock.
self reset

Generator >> next
"Generate and answer the next object in the receiver."

^ monitor critical: [
self atEnd ifFalse: [
home swapSender: thisContext sender.
continue := thisContext swapSender: continue
].
monitor signal]

]


とりあえずこれで正しい答えは返せるようになりました。

| primesGen queue |
"略"
queue := SharedQueue2 new.
100 timesRepeat: [
   [50 timesRepeat: [
      queue nextPut: primesGen next -> Processor activeProcess name.
      Processor yield]
   ] fork].
5000 timesRepeat: [result := queue next key].
result   "=> 48611 "


速度面での影響もさほどはないようです。

| primesGen result |
"略"
{[5000 timesRepeat: [result := primesGen next]] timeToRun. result}

"=> #(41 48611) "