2016-03-25 22 views
1

私はモジュールに従っています、それは平行地図をシミュレートします。なぜランダムな順序

defmodule Parallel do 

    def pmap(collection, fun) do 
    me = self 

    collection 
     |> Enum.map(fn (elem) -> 
       spawn_link fn -> send(me, { self, fun.(elem) }) end 
      end) 
     |> Enum.map(fn (pid) -> 
       receive do { ^pid, result } -> 
        result 
       end 
     end) 
    end 
end 

私が実行して、コンパイルして期待通りになりました:

:私は receive do { pid, result } ->からピン印刷オペレータを削除すると

iex(5)> Parallel.pmap 1..1000, &(&1 * &1) 
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256 ...] 

、その後、私は正しい順序でリストこれ以上を得なかっました

[1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 256, 225, 289, 361...] 

なぜピンオペレータが注文に影響しますか?

答えて

4

あなたは1,000の同時プロセスを開始しています。それぞれが終了すると、そのPIDと結果からなるメッセージが送信されます。スケジューラは決定的ではないため、メッセージがランダムな順序で受信されることがあります。

ピン演算子は、「変数は割り当てないが、パターンマッチ」を意味します。

あなたが逆の順序で来て3つのメッセージがある場合、例を考えてみましょう:最初のメッセージが到着したときに、パターンマッチが失敗し、メッセージがに格納され、{^pid, result}

{pid3, 9} 
{pid2, 4} 
{pid1, 1} 

をあなたは具体的なPIDに一致していますメールボックス。

第2の場合は同じことが起こります。

3つ目のメッセージが来たら、結果が得られ、すでにメールボックスに入っている次のpid2とのマッチングに移ります。最後に、あなたはpid3にマッチしていて、メールボックスからまっすぐに取得します。

{pid, result}を使用すると、pid変数が再割り当てされます。最初のメッセージが来ると、それは一致し、pidにはpid3という値が割り当てられます。

最後に、到着した順にメッセージのリストが表示されます。

また、ピンオペレータに関する私の他の回答を参照してください。https://stackoverflow.com/a/27975233/912225

3

要素のコレクションをマップし、コレクション内の各要素に対して新しいプロセスを作成すると、pidのリストが返されます。これらのpidは、マッピングするコレクションと同じ順序になりますすなわち、与えられたelemのpidは元のコレクションのelemと同じpidのリストにあります。これはマッピングの仕組みで、リストの各要素に操作を適用し、これらの操作の結果のリストを取得します。

ここで、pidのリストをマップします。 ^pidにマッチすると、マッピング中の現在のメッセージpidが現在のプロセスに到着するまで、コードはブロックされます。しかし、{^pid, result}メッセージは、現在のプロセスのメッセージキュー内の唯一または最初のメッセージではない可能性があります。つまり、生成されたすべてのプロセスが現在並行して実行されているため、実行した順序で結果を送信することはありません生まれたつまり、{^pid, result}を受信して​​一致すると、メッセージキューに{^pid, result}と一致するメッセージの前に他のメッセージ({pid_1, result_1}{pid_2, result_2})がある可能性があります。これらのメッセージは、Erlangで受信プロセスがどのように機能するかによって、receiveのパターンと一致しない場合はスキップされます(一致する新しいメッセージが待つまで)。あなたは{pid, result}に一致した場合

、あなたは、任意の2要素のタプルが細かいことを言っている:この場合には、pidはおそらくpidあなたは、現在のプロセスを生み出した(私は上記の話の理由により、正確にマッピングしていることはありません結果は予測不可能な順序で返送されます)。

より視覚的な表現:生成されたプロセスは、(我々が生成されたプロセスpid1pid2を呼び出すなどします)に実行を開始した後で、現在のプロセスでは、このメッセージキューを持っていると言う:

# The one on top is the first in the message queue: 
{pid3, res3} 
{pid1, res1} 
{pid4, res4} 
{pid5, res5} 
{pid2, res2} 

とし、現在pid1(つまり、Enum.map/2に渡す関数のpidpid1)をマッピングしているとします。

receive do {^pid, res} -> ...pid == pid1)を実行すると、最初のメッセージは一致しません。したがって、次のメッセージは一致し、このメッセージは一致します。 {pid3, res3}はメッセージキューに戻され、receive{pid1, res1}で実行されます。元のキューに

# The one on top is the first in the message queue: 
{pid3, res3} 
{pid4, res4} 
{pid5, res5} 
{pid2, res2} 

:メッセージキューは、次のようになります(^ピンオペレータなし){pid, res}に一致した場合、今、任意の2要素のタプルが一致します。具体的には{pid3, res3}が一致し、pidのマッピングがpid1であっても、receiveブロックが実行されます。つまり、結果のリストでは、3番目の要素(pid3が生成された)の結果が1番目の要素の結果の代わりになります。

関連する問題