関数の実行順序の検証

spawnを使ったときの挙動が分からなかったので調べてみた。下記の用途をしているソースを見つけたためである。

  • spawnでPidを生成する
  • 直後にregisterで登録済みプロセスにする
  • spwan内の処理で登録済みプロセスを扱う

ソースコード

%%%-------------------------------------------------------------------
%%% @author ksomemo
%%% @copyright (C) 2014, <COMPANY>
%%% @doc
%%% 関数の実行順序の検証
%%% @end
%%% Created : 27. 6 2014 20:16
%%%-------------------------------------------------------------------
-module(func_exe_order).
-author("ksomemo").

%% API
-export([
  main/0
]).

main() ->
  Pid0 = spawn(io, format, ["spawn/1_lambda~n"]),
  io:format("Pid0 bind ~p~n", [Pid0]),
  io:format("Pid0 after~n~n"),

  Pid1 = spawn(fun() -> io:format("spawn/1_lambda~n") end),
  io:format("Pid1 bind ~p~n", [Pid1]),
  io:format("Pid1 after~n~n"),

  Pid2 = spawn(fun sub1/0),
  io:format("Pid2 bind ~p~n", [Pid2]),
  io:format("Pid2 after1~n"),
  io:format("Pid2 after2~n"),
  io:format("Pid2 after3~n~n"),

  io:format("sub2 before~n"),
  sub2(),
  io:format("sub2 after~n"),

  [Pid0, Pid1, Pid2].

sub1() ->
  io:format("sub1~n"),
  Pid3 = spawn(fun() -> io:format("spawn/1_lambda in sub1~n") end),
  io:format("Pid3 bind ~p~n", [Pid3]),
  io:format("Pid3 after~n~n").

sub2() ->
  io:format("sub2_1~n"),
  io:format("sub2_2~n"),
  io:format("sub2_3~n").

実行結果

% erlc func_exe_order.erl
% erl -noshell -s func_exe_order main -s init stop

Pid0 bind <0.29.0>
spawn/1_lambda
Pid0 after

Pid1 bind <0.30.0>
spawn/1_lambda
Pid1 after

Pid2 bind <0.31.0>
sub1
Pid2 after1
Pid3 bind <0.32.0>
spawn/1_lambda in sub1
Pid2 after2
Pid3 after

Pid2 after3

sub2 before
sub2_1
sub2_2
sub2_3
sub2 after

まとめ

  • Pid0とPid1より、spawn/1,spawn/3でも結果は同じである
  • spawnはPidをまず返し、そのあとに処理を実行する
  • そのため、bind logが先に表示される
  • Pid2とPid3より、メインが先行して処理されるわけではない
  • 上記は Pid3 bind <0.32.0>, spawn/1_lambda in sub1 のlogより
  • 普通に関数を実行する場合は、逐次実行されるのでsub2 afterはあとに表示される
  • 実装によって冒頭に書いた流れの処理は、実装次第で意図しない挙動をしそうである

Qでもらったcomment

vの人: 2014-06-27 23:10

spawn 後の順番については忘れるべきです。軽量プロセスはマルチスレッドや非同期の処理と同様で順番保証は一切ありません。

今回は偶然処理が軽いから思った動きをしていますが少しでも重たい処理が入った時の順番保証は一切されません。シングルスレッドな実装だけを意識して実装すると痛い目に会います。

もしお時間が許すのであれば Erlang/OTP をやる前に別の言語でスレッドや非同期の処理を書いてみる事をお勧めします。それらの知識が無いと色々厳しいです。(Erlang/OTP も実際中身はただの pthread なので)
ksomemo: 2014-06-29 01:10
ありがとうございます。メッセージのやりとりのみ非同期だと思い込んでいました。

スレッドや非同期の処理の知識は、JavaやC#で少しスレッド触ったりJavaScriptでajax使った程度です…。慣れている言語で書く機会も作ってみようと思います。