ErlangでProducer-Consumerパターン

30分プログラム、その371。増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編に載っているProducer-ConsumerパターンをErlangでやってみる。
Javaと違ってwait()が無いので、キューが空のときにgetを待たせるのが結構、大変だった。やっぱり、JavaデザインパターンをそのままErlangで使うのは無理があるかもしれない。
あと元のやつと違って、キューの長さを制限してない。キューが空のときの処理を書いてたら、時間がなくなった。

使い方

1> producer_consumer:main().
Cake A(0) -->
Cake B(0) -->
        <-- eat Cake A(0)
Cake B(1) -->
Cake A(1) -->
        <-- eat Cake B(0)
        <-- eat Cake B(1)
Cake A(2) -->
Cake B(2) -->
        <-- eat Cake A(1)

ソースコード

-module(producer_consumer).
-compile([export_all]).

% ケーキを置くテーブル
table() ->
    table(queue:new(),[]).

table(Q1,Waiting) ->
    receive
	{get,From} -> 
	    case queue:out(Q1) of
		{{value,Item},Q2} ->
		    From ! {ok,Item},
		    table(Q2,Waiting);
		{empty, _ } ->
		    table(Q1,[From|Waiting])
	    end;
	{put,From,Item} ->
	    case Waiting of
		[H|T] ->
		    H ! {ok,Item},
		    From ! {ok},
		    table(Q1,T);
		[] ->
		    Q2 = queue:in(Item,Q1),
		    From ! {ok},
		    table(Q2,Waiting)
	    end
    end.

% ケーキを作ってテーブルに置くスレッド
maker(Table,Name) ->    
    maker(Table,Name,0).

maker(Table,Name,Id) ->
    Cake = io_lib:format("~s(~p)",[Name,Id]),
    Table ! {put,self(),Cake},
    io:format("~s -->~n",[Cake]),
    receive
      {ok} ->
	    timer:sleep(2000),
	    maker(Table,Name,Id+1)
    end.

% テーブルに置かれたケーキを食べるスレッド
eater(Table) ->
    timer:sleep(1000),
    Table ! {get,self()},
    receive
      {ok,Item} -> 
	    io:format("\t<-- eat ~s~n",[Item]),
	    eater(Table)
    end.

main() ->
    Table = spawn(fun () -> table() end),
    spawn(fun () -> maker(Table,"Cake A") end),
    spawn(fun () -> maker(Table,"Cake B") end),
    eater(Table).