檢視原始碼 gen_statem 行為 (stdlib v6.2)

通用的狀態機行為。

gen_statem 提供了一個通用的狀態機行為,自 Erlang/OTP 20.0 起取代了它的前身 gen_fsm,並且應該用於新的程式碼。gen_fsm 行為仍然保留在 OTP 中「原樣」,以避免破壞使用它的舊程式碼。

使用此模組實現的通用狀態機伺服器程序 (gen_statem) 具有一組標準的介面函數,並包括追蹤和錯誤回報的功能。它也適用於 OTP 監管樹。如需更多資訊,請參閱 OTP 設計原則

注意

如果您是 gen_statem 的新手,並且想要了解概念和操作的概述,建議閱讀位於使用者指南 OTP 設計原則 中的 gen_statem 行為 章節。本參考手冊的重點是正確和完整,這可能會讓人難以見樹又見林。

功能

gen_statem 具有 gen_fsm 所擁有的相同功能,並新增了一些非常有用的功能

支援兩種 回呼模式

  • state_functions - 用於有限狀態機 (類似 gen_fsm),這要求狀態為原子,並將該狀態用作目前回呼函數的名稱,arity 為 3。
  • handle_event_function - 允許狀態為任何詞彙,並將 handle_event/4 用作所有狀態的回呼函數。

gen_statem 的回呼模式與 gen_fsm 的回呼模式不同,但從 gen_fsm 重寫為 gen_statem 仍然相當容易。請參閱 gen_fsm 文件開頭的 重寫指南

回呼模組

gen_statem 假設所有特定部分都位於匯出預定義函數集的回呼模組中。行為函數和回呼函數之間的關係如下

gen_statem module            Callback module
-----------------            ---------------
gen_statem:start
gen_statem:start_monitor
gen_statem:start_link -----> Module:init/1

Server start or code change
                      -----> Module:callback_mode/0
                      selects callback mode

gen_statem:stop
Supervisor exit
Callback failure      -----> Module:terminate/3

gen_statem:call
gen_statem:cast
gen_statem:send_request
erlang:send
erlang:'!'            -----> Module:StateName/3
                   or -----> Module:handle_event/4
                   depending on callback mode

Release upgrade/downgrade
(code change)
                      -----> Module:code_change/4

狀態回呼

gen_statem 中特定 狀態狀態回呼是針對此狀態中的所有事件呼叫的回呼函數。它會根據回呼模組透過回呼函數 Module:callback_mode/0 定義的 回呼模式 來選擇。

回呼模式state_functions 時,狀態必須為原子,並用作狀態回呼名稱;請參閱 Module:StateName/3。這會將特定狀態的所有程式碼共同定位在一個函數中,因為 gen_statem 引擎會根據狀態名稱進行分支。請注意,回呼函數 Module:terminate/3 使狀態名稱 terminate 在此模式中無法使用。

回呼模式handle_event_function 時,狀態可以是任何詞彙,而狀態回呼名稱為 Module:handle_event/4。這使得根據狀態或事件進行分支變得容易。請注意您在哪些狀態中處理哪些事件,以免意外地永遠延遲事件,而造成無限的忙碌迴圈。

事件類型

事件具有不同的 類型,因此回呼函數在處理事件時可以知道事件的來源。外部事件callcastinfo。內部事件為 timeoutinternal

事件處理

gen_statem 收到程序訊息時,它會轉換為事件,並使用該事件作為兩個引數:類型和內容來呼叫 狀態回呼。當 狀態回呼 處理完事件後,它會返回 gen_statem,後者會執行狀態轉換。如果此狀態轉換到不同的狀態,即:NextState =/= State,則為狀態變更

轉換動作

狀態回呼 可以傳回 轉換動作,讓 gen_statem狀態轉換期間執行,例如設定逾時或回覆呼叫。

回覆呼叫

關於如何回覆呼叫,請參閱 gen_statem:call/2,3。回覆可以從任何狀態回呼傳送,而不僅僅是收到請求事件的回呼。

事件延遲

其中一種可能的轉換動作是延遲目前的事件。然後它將不會在目前狀態中處理。gen_statem 引擎會維護一個事件佇列,分為延遲事件和仍要處理 (尚未呈現) 的事件。在狀態變更之後,佇列會以延遲的事件重新開始。

gen_statem 事件佇列模型足以模擬具有選擇性接收的正常程序訊息佇列。延遲事件相當於不在 receive 陳述式中比對它,而變更狀態相當於進入新的 receive 陳述式。

事件插入

狀態回呼 可以使用 轉換動作 next_event 來插入事件,而且此類事件會插入到事件佇列中,作為下一個呼叫 狀態回呼 的事件。也就是說,如同它是最舊的傳入事件。專用的 event_type/0 internal 可用於此類事件,使其可以安全地將它們與外部事件區分開來。

插入事件取代了呼叫您自己的狀態處理函數的技巧,例如,您通常必須在 gen_fsm 中採用此技巧,以強制在其他事件之前處理插入的事件。

注意

如果您延遲事件,並且 (違反良好實務) 直接呼叫不同的狀態回呼,則不會重試延遲的事件,因為沒有狀態變更

不要直接呼叫狀態回呼,而是執行狀態變更。這會讓 gen_statem 引擎重試延遲的事件。

狀態變更中插入事件也會觸發新的狀態回呼,以便在收到任何外部事件之前呼叫該事件。

狀態進入呼叫

每當輸入新的狀態時,gen_statem 引擎都可以自動對 狀態回呼 進行特殊呼叫;請參閱 state_enter/0。這是為了編寫所有狀態條目共用的程式碼。另一種方法是在狀態轉換時明確插入事件,和/或使用專用的狀態轉換函數,但是您必須在每次狀態轉換到需要它的狀態時記住這一點。

有關狀態轉換的詳細資訊,請參閱 transition_option/0 類型。

休眠

gen_statem 程序可以進入休眠狀態;請參閱 proc_lib:hibernate/3。當 狀態回呼Module:init/1 在傳回的 Actions 清單中指定 hibernate 時,就會執行此動作。當伺服器預期會閒置很長時間時,此功能可用於回收程序堆積記憶體。但是,請謹慎使用它,因為每次事件後使用休眠可能會太過昂貴;請參閱 erlang:hibernate/3

還有一個伺服器啟動選項 {hibernate_after, Timeout} 用於 start/3,4start_link/3,4start_monitor/3,4enter_loop/4,5,6,可用於自動休眠伺服器。

回呼失敗

如果回呼函數失敗或傳回錯誤的值,則 gen_statem 會終止。但是,類別為 throw 的例外情況不會被視為錯誤,而是視為所有回呼函數的有效傳回值。

系統訊息和 sys 模組

gen_statem 會如 sys 中所述處理系統訊息。sys 模組可用於偵錯 gen_statem。透過 轉換動作 傳送的回覆會記錄下來,但透過 reply/1,2 傳送的回覆則不會記錄下來。

捕獲退出

如同所有 gen_* 行為,gen_statem 程序不會自動捕獲退出訊號;這必須在回呼模組中明確初始化 (透過呼叫 process_flag(trap_exit, true),最好是從 init/1 呼叫)。

伺服器終止

如果 gen_statem 程序終止,例如由於回呼函數傳回 {stop, Reason} 的結果,則會將帶有此 Reason 的退出訊號傳送至連結的程序和連接埠。如需使用退出訊號進行錯誤處理的詳細資訊,請參閱參考手冊中的 程序

注意

有關分散式訊號的一些重要資訊,請參閱Erlang 參考手冊程序章節中的 分散式封鎖訊號 章節。封鎖訊號可能會導致 gen_statem 中的呼叫逾時顯著延遲。

錯誤的引數

除非另有說明,否則如果指定的 gen_statem 不存在,或指定了錯誤的引數,則此模組中的所有函數都會失敗。

範例

以下範例顯示了一個簡單的按鈕模型,用於使用 回呼模式 state_functions 實作的切換按鈕。您可以按下按鈕,它會回覆是否開啟或關閉,而且您可以要求計算按下按鈕開啟的次數。

按鈕狀態圖

---
title: Pushbutton State Diagram
---
stateDiagram-v2
    [*]  --> off
    off  --> on  : push\n* Increment count\n* Reply 'on'
    on   --> off : push\n* Reply 'off'

未在狀態圖中顯示

  • API 函數 push() 會產生類型為 callpush 事件。
  • API 函式 get_count() 會產生一個類型為 callget_count 事件,此事件會在所有狀態中被處理,並回覆目前的計數值。
  • 未知的事件會被忽略並丟棄。
  • 有樣板程式碼用於啟動、停止、終止、程式碼變更、初始化,以及設定回呼模式為 state_functions 等等。

按鈕程式碼

以下是完整的回呼模組檔案 pushbutton.erl

-module(pushbutton).
-behaviour(gen_statem).

-export([start/0,push/0,get_count/0,stop/0]).
-export([terminate/3,code_change/4,init/1,callback_mode/0]).
-export([on/3,off/3]).

name() -> pushbutton_statem. % The registered server name

%% API.  This example uses a registered name name()
%% and does not link to the caller.
start() ->
    gen_statem:start({local,name()}, ?MODULE, [], []).
push() ->
    gen_statem:call(name(), push).
get_count() ->
    gen_statem:call(name(), get_count).
stop() ->
    gen_statem:stop(name()).

%% Mandatory callback functions
terminate(_Reason, _State, _Data) ->
    void.
code_change(_Vsn, State, Data, _Extra) ->
    {ok,State,Data}.
init([]) ->
    %% Set the initial state + data.  Data is used only as a counter.
    State = off, Data = 0,
    {ok,State,Data}.
callback_mode() -> state_functions.

%%% state callback(s)

off({call,From}, push, Data) ->
    %% Go to 'on', increment count and reply
    %% that the resulting status is 'on'
    {next_state,on,Data+1,[{reply,From,on}]};
off(EventType, EventContent, Data) ->
    handle_event(EventType, EventContent, Data).

on({call,From}, push, Data) ->
    %% Go to 'off' and reply that the resulting status is 'off'
    {next_state,off,Data,[{reply,From,off}]};
on(EventType, EventContent, Data) ->
    handle_event(EventType, EventContent, Data).

%% Handle events common to all states
handle_event({call,From}, get_count, Data) ->
    %% Reply with the current count
    {keep_state,Data,[{reply,From,Data}]};
handle_event(_, _, Data) ->
    %% Ignore all other events
    {keep_state,Data}.

以下是執行時的 Shell 會話

1> pushbutton:start().
{ok,<0.36.0>}
2> pushbutton:get_count().
0
3> pushbutton:push().
on
4> pushbutton:get_count().
1
5> pushbutton:push().
off
6> pushbutton:get_count().
1
7> pushbutton:stop().
ok
8> pushbutton:push().
** exception exit: {noproc,{gen_statem,call,[pushbutton_statem,push,infinity]}}
     in function  gen:do_for_proc/2 (gen.erl, line 261)
     in call from gen_statem:call/3 (gen_statem.erl, line 386)

為了比較風格,以下是使用 回呼模式 handle_event_function 的相同範例,更確切地說,是取代上方 pushbutton.erl 範例檔案中函式 init/1 之後的程式碼

callback_mode() -> handle_event_function.

%%% state callback(s)

handle_event({call,From}, push, off, Data) ->
    %% Go to 'on', increment count and reply
    %% that the resulting status is 'on'
    {next_state,on,Data+1,[{reply,From,on}]};
handle_event({call,From}, push, on, Data) ->
    %% Go to 'off' and reply that the resulting status is 'off'
    {next_state,off,Data,[{reply,From,off}]};
%%
%% Event handling common to all states
handle_event({call,From}, get_count, State, Data) ->
    %% Reply with the current count
    {next_state,State,Data,[{reply,From,Data}]};
handle_event(_, _, State, Data) ->
    %% Ignore all other events
    {next_state,State,Data}.

注意

API 變更

另請參閱

gen_eventgen_fsmgen_serverproc_libsupervisorsys

摘要

類型

用於狀態轉換或啟動伺服器的動作。

每個狀態一個函式或一個通用的事件處理常式。

伺服器的通用狀態資料。

用於任何回呼的動作:休眠、逾時或回覆。

來自事件來源的事件酬載,傳遞至狀態回呼

處理事件後,來自狀態回呼的回傳值。

等待事件的時間長度。

所有事件類型:外部逾時internal

來自 callcast 或一般程序訊息的事件;「info」。

描述伺服器狀態的對應。

call 事件的回覆目的地。

等待具名逾時事件的時間長度。

讓伺服器程序休眠。

延後事件,以便稍後處理。

將回覆與對應請求關聯的句柄。

不透明的請求識別碼。詳情請參閱 send_request/2

不透明的請求識別碼集合(request_id/0)。

非同步呼叫的回應逾時。

伺服器名稱規範:localglobal 或已註冊的 via

伺服器規範:pid/0 或已註冊的 server_name/0

來自 start_monitor/3,4 函式的回傳值。

狀態名稱或狀態詞彙。

用於狀態進入呼叫回呼模式修飾符:原子 state_enter

狀態進入呼叫之後,來自狀態回呼的回傳值。

回呼模式 state_functions 中的狀態名稱。

在目前狀態中等待的時間長度。

事件逾時、通用逾時或狀態逾時。

比原始設定為「無限」更明確的取消逾時方式。

逾時計時器啟動選項,可選擇絕對的到期時間。

更新 EventContent,而不影響到期時間。

動作設定的狀態轉換選項。

回呼

選取回呼模式,以及可能的 狀態進入呼叫

在程式碼變更後更新 statedata

格式化/限制狀態值。

format_status/2 已棄用

格式化/限制狀態值。

回呼模式 handle_event_function 中的狀態回呼

初始化狀態機器。

回呼模式 state_functions 中的狀態回呼

處理狀態機器終止。

函式

呼叫伺服器:傳送請求並等待回應。

將事件發送至伺服器。

檢查收到的訊息是否為請求回應。

檢查收到的訊息是否為集合中的請求回應。

使呼叫程序成為 gen_statem 伺服器。

使呼叫程序成為 gen_statem 伺服器。

接收請求回應。

接收集合中的請求回應。

傳送一個或多個 call 回覆。

傳送 call ReplyFrom

在集合中儲存請求識別碼。

建立一個空的請求識別碼集合。

返回 ReqIdCollection 中請求識別碼的數量。

將請求識別碼集合轉換為列表。

傳送一個非同步的 call 請求。

傳送一個非同步的 call 請求,並將其加入請求識別碼集合。

啟動一個伺服器,既不連結也不註冊。

啟動一個已註冊但未連結的伺服器。

啟動一個已連結但未註冊的伺服器。

啟動一個已連結且已註冊的伺服器。

啟動一個已監控但既不連結也不註冊的伺服器。

啟動一個已監控且已註冊但未連結的伺服器。

停止一個伺服器。

等待請求的回應。

等待集合中任何請求的回應。

類型

連結到此類型

action()

檢視原始碼 (自 OTP 19.0 起)
-type action() ::
          postpone |
          {postpone, Postpone :: postpone()} |
          {next_event, EventType :: event_type(), EventContent :: event_content()} |
          {change_callback_module, NewModule :: module()} |
          {push_callback_module, NewModule :: module()} |
          pop_callback_module |
          enter_action().

用於狀態轉換或啟動伺服器的動作。

這些轉換動作可以透過在呼叫 狀態回呼 時,從 Module:init/1 返回它們,或將它們傳遞給 enter_loop/4,5,6 來呼叫。它們在狀態進入呼叫不允許使用。

動作會依容器列表的順序執行。

設定 轉換選項 的動作會覆蓋任何先前相同類型的選項,因此容器列表中的最後一個會生效。例如,最後一個 postpone/0 會覆蓋列表中任何先前的 postpone/0

  • {postpone, Value} - 為此狀態轉換設定 transition_option() postpone/0。當從 Module:init/1 返回或傳遞給 enter_loop/4,5,6 時,此動作會被忽略,因為在這些情況下沒有要延遲的事件。

    postpone 等同於 {postpone, true}

  • {next_event, EventType, EventContent} - 此動作不會設定任何 transition_option(),而是儲存指定的 EventTypeEventContent,以便在所有動作執行後插入。

    儲存的事件會插入佇列中,作為在任何已排隊的事件之前要處理的下一個事件。這些儲存的事件的順序會被保留,因此容器列表中的第一個 next_event 會成為第一個要處理的事件。

    當您想要可靠地將以這種方式插入的事件與任何外部事件區分開來時,應該使用 internal 類型的事件。

  • {change_callback_module, NewModule} - 將回呼模組變更為 NewModule,在呼叫所有後續的狀態回呼時將會使用該模組。
    自 OTP 22.3 起。

    gen_statem 引擎將透過在下一個狀態回呼之前呼叫 NewModule:callback_mode/0 來找出 NewModule回呼模式

    變更回呼模組不會以任何方式影響狀態轉換,它只會變更處理事件的模組。請注意,NewModule 中的所有相關回呼函數(例如狀態回呼NewModule:code_change/4NewModule:format_status/1NewModule:terminate/3)都必須能夠處理舊模組的狀態和資料。

  • {push_callback_module, NewModule} - 將目前的回呼模組推送到回呼模組的內部堆疊頂部,並將回呼模組變更為 NewModule。否則,就像上面的 {change_callback_module, NewModule} 一樣。
    自 OTP 22.3 起。

  • pop_callback_module - 從回呼模組的內部堆疊中彈出頂部模組,並將回呼模組變更為彈出的模組。如果堆疊為空,伺服器會失敗。否則,就像上面的 {change_callback_module, NewModule} 一樣。
    自 OTP 22.3 起。

連結到此類型

callback_mode()

檢視原始碼 (未匯出) (自 OTP 19.1 起)
-type callback_mode() :: state_functions | handle_event_function.

每個狀態一個函式或一個通用的事件處理常式。

回呼模式是使用 Module:callback_mode/0 的回傳值選取的。

函數 Module:callback_mode/0 會在啟動 gen_statem 時、程式碼變更後以及在使用任何動作 change_callback_modulepush_callback_modulepop_callback_module 變更回呼模組後呼叫。結果會快取以供後續呼叫 狀態回呼

連結到此類型

callback_mode_result()

檢視原始碼 (自 OTP 19.2 起)
-type callback_mode_result() :: callback_mode() | [callback_mode() | state_enter()].

來自 Module:callback_mode/0 的回傳值。

這是從 Module:callback_mode/0 返回的類型,它會選取回呼模式以及是否執行 狀態進入呼叫

連結到此類型

data()

檢視原始碼 (未匯出) (自 OTP 19.0 起)
-type data() :: term().

伺服器的通用狀態資料。

狀態機實作在其中儲存所需任何伺服器資料的詞彙。這與 state/0 本身的不同之處在於,此資料的變更不會導致延遲的事件重試。因此,如果此資料的變更會變更處理的事件集,則該資料項目應為 state/0 的一部分。

連結到此類型

enter_action()

檢視原始碼 (自 OTP 19.0 起)
-type enter_action() ::
          hibernate | {hibernate, Hibernate :: hibernate()} | timeout_action() | reply_action().

用於任何回呼的動作:休眠、逾時或回覆。

當允許 action/0 時,也允許這些轉換動作,且可以從狀態進入呼叫中呼叫,並且可以透過從 狀態回呼、從 Module:init/1 返回它們,或將它們傳遞給 enter_loop/4,5,6 來呼叫。

動作會依容器列表的順序執行。

設定 轉換選項 的動作會覆蓋任何先前相同類型的選項,因此容器列表中的最後一個會生效。例如,最後一個 event_timeout/0 會覆蓋列表中任何先前的 event_timeout/0

連結到此類型

enter_loop_opt()

檢視原始碼 (自 OTP 19.0 起)
-type enter_loop_opt() ::
          {hibernate_after, HibernateAfterTimeout :: timeout()} | {debug, Dbgs :: [sys:debug_option()]}.

用於 enter_loop/4,5,6start/3,4start_link/3,4start_monitor/3,4 函式的伺服器啟動選項

請參閱 start_link/4

連結到此類型

event_content()

檢視原始碼 (未匯出) (自 OTP 19.0 起)
-type event_content() :: term().

來自事件來源的事件酬載,傳遞至狀態回呼

請參閱 event_type,其中描述了不同事件類型的來源,這也是事件內容的來源。

連結到此類型

event_handler_result(StateType)

檢視原始碼 (自 OTP 19.0 起)
-type event_handler_result(StateType) :: event_handler_result(StateType, term()).
連結到此類型

event_handler_result(StateType, DataType)

檢視原始碼 (自 OTP 19.0 起)
-type event_handler_result(StateType, DataType) ::
          {next_state, NextState :: StateType, NewData :: DataType} |
          {next_state, NextState :: StateType, NewData :: DataType, Actions :: [action()] | action()} |
          state_callback_result(action(), DataType).

處理事件後,來自狀態回呼的回傳值。

如果 回呼模式state_functions,則 StateTypestate_name/0,如果 回呼模式handle_event_function,則 StateTypestate/0

  • {next_state, NextState, NewData [, Actions]} - gen_statem 執行狀態轉換NextState (可能與目前狀態相同),將 NewData 設定為目前的伺服器 data/0,並執行所有 Actions。如果 NextState =/= CurrentState,則狀態轉換狀態變更
連結到此類型

event_timeout()

檢視原始碼 (未匯出) (自 OTP 19.0 起)
-type event_timeout() :: Time :: timeout() | integer().

等待事件的時間長度。

啟動由 timeout_action/0 設定的計時器 Time,或 {timeout, Time, EventContent [, Options]}

當計時器到期時,會產生一個 event_type/0 timeout 的事件。關於 TimeOptions 如何解讀,請參閱 erlang:start_timer/4。未來不一定會支援 erlang:start_timer/4Options

任何到達的事件都會取消此逾時。請注意,重試或插入的事件都算作已到達。如果狀態逾時零事件在此逾時請求之前產生,也算作已到達。

如果 Timeinfinity,則不會啟動計時器,因為它永遠不會到期。

如果 Time 是相對時間且為 0,則實際上不會啟動計時器,而是將逾時事件排入佇列,以確保它在任何尚未接收到的外部事件之前處理,但在已排隊的事件之後處理。

請注意,不需要也不可能取消此逾時,因為它會被任何其他事件自動取消,這表示每當調用一個可能想要取消此逾時的回調函式時,計時器已經被取消或已到期。

可以使用 {timeout, update, NewEventContent} 動作更新計時器 EventContent,而不會影響到期時間。

連結到此類型

event_type()

檢視原始碼 (自 OTP 19.0 起)
-type event_type() :: external_event_type() | timeout_event_type() | internal.

所有事件類型:外部逾時internal

internal 事件只能由狀態機本身透過 *轉換動作* next_event 產生。

連結到此類型

external_event_type()

檢視原始碼 (未匯出) (自 OTP 19.0 起)
-type external_event_type() :: {call, From :: from()} | cast | info.

來自 callcast 或一般程序訊息的事件;「info」。

類型 {call, From} 源自 API 函式 call/2,3send_request/2。該事件包含 From,這是要透過 reply_action/0reply/2,3 呼叫回覆的對象。

類型 cast 源自 API 函式 cast/2

類型 info 源自傳送到 gen_statem 程序的一般程序訊息。

連結到此類型

format_status()

檢視原始碼 (自 OTP 19.0 起)
-type format_status() ::
          #{state => state(),
            data => data(),
            reason => term(),
            queue => [{event_type(), event_content()}],
            postponed => [{event_type(), event_content()}],
            timeouts => [{timeout_event_type(), event_content()}],
            log => [sys:system_event()]}.

描述伺服器狀態的對應。

鍵如下:

  • state - 目前的狀態。
  • data - 狀態資料。
  • reason - 導致程序終止的原因。
  • queue - 事件佇列。
  • postponed - 延後事件的佇列。
  • timeouts - 活動中的 逾時
  • log - 伺服器的 sys log

新的關聯可能會新增到狀態映射中,恕不另行通知。

連結到此類型

from()

檢視原始碼 (自 OTP 19.0 起)
-type from() :: {To :: pid(), Tag :: reply_tag()}.

call 事件的回覆目的地。

使用例如 {reply, From, Reply} 動作回覆已使用 call/2,3 呼叫 gen_statem 伺服器的程序時使用的目的地。

連結到此類型

generic_timeout()

檢視原始碼 (未匯出) (自 OTP 20.0 起)
-type generic_timeout() :: Time :: timeout() | integer().

等待具名逾時事件的時間長度。

啟動由 timeout_action/0 設定的計時器 {{timeout, Name}, Time, EventContent [, Options]}

當計時器到期時,會產生一個 event_type/0 {timeout, Name} 的事件。關於 TimeOptions 如何解讀,請參閱 erlang:start_timer/4。未來不一定會支援 erlang:start_timer/4Options

如果 Timeinfinity,則不會啟動計時器,因為它永遠不會到期。

如果 Time 是相對時間且為 0,則實際上不會啟動計時器,而是將逾時事件排入佇列,以確保它在任何尚未接收到的外部事件之前處理。

當計時器正在執行時,使用相同的 Name 設定計時器會使用新的逾時值重新啟動它。因此,可以透過將其設定為 infinity 來取消特定的逾時。也可以使用 {{timeout, Name}, cancel} 動作更明確地取消它。

可以使用 {{timeout, Name}, update, NewEventContent} 動作更新計時器 EventContent,而不會影響到期時間。

連結到此類型

hibernate()

檢視原始碼 (未匯出) (自 OTP 19.0 起)
-type hibernate() :: boolean().

讓伺服器程序休眠。

如果為 true,則會在進入 receive 以等待新的外部事件之前,呼叫 proc_lib:hibernate/3 來休眠 gen_statem

還有一個伺服器啟動選項 {hibernate_after, Timeout},用於自動休眠。

注意

如果請求休眠時有排隊的事件要處理,則會進行最佳化,方法是不休眠,而是呼叫 erlang:garbage_collect/0,以更有效的方式模擬 gen_statem 進入休眠狀態並立即被排隊的事件喚醒。

連結到此類型

init_result(StateType)

檢視原始碼 (自 OTP 19.0 起)
-type init_result(StateType) :: init_result(StateType, term()).
連結到此類型

init_result(StateType, DataType)

檢視原始碼 (自 OTP 19.0 起)
-type init_result(StateType, DataType) ::
          {ok, State :: StateType, Data :: DataType} |
          {ok, State :: StateType, Data :: DataType, Actions :: [action()] | action()} |
          ignore |
          {stop, Reason :: term()} |
          {error, Reason :: term()}.

來自 Module:init/1 的回傳值。

對於成功的初始化,State 是初始 state/0,而 Datagen_statem 的初始伺服器 data/0

當進入第一個 狀態時,會執行 Actions,就像執行 狀態回調一樣,只是動作 postpone 會強制設為 false,因為沒有事件可以延後。

對於不成功的初始化,應使用 {stop, Reason}{error, Reason}ignore;請參閱 start_link/3,4

自 OTP 26.0 起 已允許 {error, Reason}

{ok, ...} 元組自 OTP 19.1 起存在,在此之前它們沒有 ok 標籤。這是因為在 OTP 20.0 中 gen_statem 取代 gen_fsm 之前。

連結到此類型

postpone()

檢視原始碼 (未匯出) (自 OTP 19.0 起)
-type postpone() :: boolean().

延後事件,以便稍後處理。

如果為 true,則會延後目前的事件。在 *狀態變更*(NextState =/= State)之後,會重試。

連結到此類型

reply_action()

檢視原始碼 (自 OTP 19.0 起)
-type reply_action() :: {reply, From :: from(), Reply :: term()}.

回覆 call/2,3

可以透過從 狀態回調、從 Module:init/1 或透過將其傳遞到 enter_loop/4,5,6 來調用此 *轉換動作*。

它不會設定任何 transition_option(),而是回覆在 call/3 中等待回覆的呼叫者。From 必須是來自呼叫 狀態回調 時的 {call, From} 參數中的術語。

請注意,從 Module:init/1enter_loop/4,5,6 使用此動作將會很奇怪,簡直是巫術邊緣,因為在此伺服器中沒有先前對 狀態回調 的呼叫。

此不透明物件的連結

reply_tag()

檢視原始碼 (自 OTP 19.0 起)
-opaque reply_tag()

將回覆與對應請求關聯的句柄。

此不透明物件的連結

request_id()

檢視原始碼 (自 OTP 19.0 起)
-opaque request_id()

不透明的請求識別碼。詳情請參閱 send_request/2

此不透明物件的連結

request_id_collection()

檢視原始碼 (自 OTP 19.0 起)
-opaque request_id_collection()

不透明的請求識別碼集合(request_id/0)。

每個請求識別碼都可以與使用者選擇的標籤相關聯。如需更多資訊,請參閱 reqids_new/0

連結到此類型

response_timeout()

檢視原始碼 (未匯出) (自 OTP 19.0 起)
-type response_timeout() :: timeout() | {abs, integer()}.

非同步呼叫的回應逾時。

用來設定等待回應的時間限制,適用於 receive_response/2receive_response/3wait_response/2wait_response/3。時間單位為 millisecond (毫秒)。

目前可用的值:

  • 0..4294967295 - 相對於目前時間的逾時時間,以毫秒為單位。

  • infinity - 無限逾時。也就是說,操作永遠不會逾時。

  • {abs, Timeout} - 以毫秒為單位的絕對 Erlang 單調時間逾時。也就是說,當 erlang:monotonic_time(millisecond) 返回的值大於或等於 Timeout 時,操作將會逾時。Timeout 不得指定超過未來 4294967295 毫秒的時間。當您有一組請求的完整集合 ( request_id_collection/0) 的回應期限時,使用絕對值指定逾時時間特別方便,因為您不必重複重新計算直到期限的相對時間。

連結到此類型

server_name()

檢視原始碼 (自 OTP 19.0 起)
-type server_name() ::
          {local, atom()} | {global, GlobalName :: term()} | {via, RegMod :: module(), Name :: term()}.

伺服器名稱規範:localglobal 或已註冊的 via

啟動 gen_statem 伺服器時使用的名稱規格。請參閱下方的 start_link/3server_ref/0

連結到此類型

server_ref()

檢視原始碼 (自 OTP 19.0 起)
-type server_ref() ::
          pid() |
          (LocalName :: atom()) |
          {Name :: atom(), Node :: atom()} |
          {global, GlobalName :: term()} |
          {via, RegMod :: module(), ViaName :: term()}.

伺服器規範:pid/0 或已註冊的 server_name/0

用於 call/2,3 中以指定伺服器。

它可以是:

  • pid() | LocalName - gen_statem 已在本機註冊。

  • {Name, Node} - gen_statem 已在另一個節點上在本機註冊。

  • {global, GlobalName} - gen_statem 已在 global 中全域註冊。

  • {via, RegMod, ViaName} - gen_statem 已在替代程序註冊表中註冊。註冊回呼模組 RegMod 應匯出函式 register_name/2unregister_name/1whereis_name/1send/2,這些函式的行為應與 global 中的對應函式相同。因此,{via, global, GlobalName}{global, GlobalName} 相同。

連結到此類型

start_mon_ret()

檢視原始碼 (自 OTP 19.0 起)
-type start_mon_ret() :: {ok, {pid(), reference()}} | ignore | {error, term()}.

來自 start_monitor/3,4 函式的回傳值。

start_link/4 相同,但成功回傳會將程序 ID 和 監控參考 包裝在 {ok, {pid(),reference()}} 元組中。

連結到此類型

start_opt()

檢視原始碼 (自 OTP 19.0 起)
-type start_opt() ::
          {timeout, Time :: timeout()} | {spawn_opt, [proc_lib:start_spawn_option()]} | enter_loop_opt().

用於 start/3,4start_link/3,4start_monitor/3,4 函式的伺服器啟動選項

請參閱 start_link/4

連結到此類型

start_ret()

檢視原始碼 (自 OTP 19.0 起)
-type start_ret() :: {ok, pid()} | ignore | {error, term()}.

來自 start/3,4start_link/3,4 函式的回傳值

請參閱 start_link/4

連結到此類型

state()

檢視原始碼 (未匯出) (自 OTP 19.0 起)
-type state() :: state_name() | term().

狀態名稱或狀態詞彙。

如果 回呼模式handle_event_function,則狀態可以是任何項目。在狀態變更 ( NextState =/= State ) 之後,所有延遲的事件都會重試。

假設比較兩個狀態的嚴格相等性是一個快速的操作,因為對於每個狀態轉換gen_statem 引擎都必須推斷它是否為狀態變更

注意

通常,狀態項目越小,比較速度就越快。

請注意,如果為狀態轉換回傳「相同」的狀態項目 (或使用不帶 NextState 欄位的回傳動作),則相等性的比較始終很快,因為可以從項目處理常式中看到這一點。

但是,如果回傳新建立的狀態項目,則必須遍歷舊的和新的狀態項目,直到找到不相等之處,或直到兩個項目都已完全遍歷為止。

因此,可以使用比較速度很快的大型狀態項目,但很容易意外弄糟。使用小型狀態項目是安全選擇。

連結到此類型

state_callback_result(ActionType, DataType)

檢視原始碼 (未匯出) (自 OTP 19.0 起)
-type state_callback_result(ActionType, DataType) ::
          {keep_state, NewData :: DataType} |
          {keep_state, NewData :: DataType, Actions :: [ActionType] | ActionType} |
          keep_state_and_data |
          {keep_state_and_data, Actions :: [ActionType] | ActionType} |
          {repeat_state, NewData :: DataType} |
          {repeat_state, NewData :: DataType, Actions :: [ActionType] | ActionType} |
          repeat_state_and_data |
          {repeat_state_and_data, Actions :: [ActionType] | ActionType} |
          stop |
          {stop, Reason :: term()} |
          {stop, Reason :: term(), NewData :: DataType} |
          {stop_and_reply, Reason :: term(), Replies :: [reply_action()] | reply_action()} |
          {stop_and_reply,
           Reason :: term(),
           Replies :: [reply_action()] | reply_action(),
           NewData :: DataType}.

來自任何狀態回呼的回傳值。

如果狀態回呼是使用 狀態進入呼叫 呼叫的,則 ActionTypeenter_action/0;如果狀態回呼是使用事件呼叫的,則為 action/0

  • {keep_state, NewData [, Actions]} - 與 {next_state, CurrentState, NewData [, Actions]} 相同。

  • keep_state_and_data | {keep_state_and_data, Actions} - 與 {keep_state, CurrentData [, Actions]} 相同。

  • {repeat_state, NewData [, Actions]} - 如果 gen_statem 使用 狀態進入呼叫 執行,則會重複狀態進入呼叫,請參閱類型 transition_option/0。除此之外,{repeat_state, NewData [, Actions]}{keep_state, NewData [, Actions]} 相同。

  • repeat_state_and_data | {repeat_state_and_data, Actions} - 與 {repeat_state, CurrentData [, Actions]} 相同。

  • {stop, Reason [, NewData]} - 透過使用 ReasonNewData (如果指定) 呼叫 Module:terminate/3 來終止 gen_statem。包含此原因的退出訊號會傳送至連結的程序和連接埠。

  • stop - 與 {stop, normal} 相同。

  • {stop_and_reply, Reason, Replies [, NewData]} - 傳送所有 Replies,然後像使用 {stop, Reason [, NewData]} 一樣終止 gen_statem

所有這些項目都是元組或原子,並且在 gen_statem 的所有未來版本中都將如此。

連結到此類型

state_enter()

檢視原始碼 (未匯出) (自 OTP 19.2 起)
-type state_enter() :: state_enter.

用於狀態進入呼叫回呼模式修飾符:原子 state_enter

兩種回呼模式都可以使用狀態進入呼叫,這可以透過將 state_enter 旗標新增至 回呼模式Module:callback_mode/0 回傳的值中來選取。

如果 Module:callback_mode/0 回傳一個包含 state_enter 的清單,則 gen_statem 引擎會在每次狀態變更時 (也就是 NextState =/= CurrentState),使用引數 (enter, OldState, Data)(enter, OldState, State, Data) 呼叫 狀態回呼,具體取決於 回呼模式

這看起來像一個事件,但實際上是在先前的 狀態回呼 回傳之後,以及在任何事件傳遞至新的 狀態回呼 之前執行的呼叫。請參閱 Module:StateName/3Module:handle_event/4。透過從狀態回呼回傳 repeat_staterepeat_state_and_data 動作,可以在不進行狀態變更的情況下重複狀態進入呼叫

如果 Module:callback_mode/0 未回傳包含 state_enter 的清單,則不會執行狀態進入呼叫

如果 Module:code_change/4 應轉換狀態,則將其視為狀態重新命名,而不是狀態變更,這不會導致狀態進入呼叫

請注意,在進入初始狀態之前將會執行狀態進入呼叫,這可以視為從無狀態到初始狀態的狀態變更。在這種情況下,OldState =:= State,這不會在後續的狀態變更中發生,但會在重複狀態進入呼叫時發生。

連結到此類型

state_enter_result(State)

檢視原始碼 (自 OTP 19.0 起)
-type state_enter_result(State) :: state_enter_result(State, term()).
連結到此類型

state_enter_result(State, DataType)

檢視原始碼 (自 OTP 19.0 起)
-type state_enter_result(State, DataType) ::
          {next_state, State, NewData :: DataType} |
          {next_state, State, NewData :: DataType, Actions :: [enter_action()] | enter_action()} |
          state_callback_result(enter_action(), DataType).

狀態進入呼叫之後,來自狀態回呼的回傳值。

State 是目前的狀態,由於使用 狀態進入呼叫 呼叫狀態回呼,因此無法變更。

  • {next_state, State, NewData [, Actions]} - gen_statem 會執行到 State 的狀態轉換,該狀態必須等於目前的狀態,設定 NewData,並執行所有 Actions
連結到此類型

state_name()

檢視原始碼 (未匯出) (自 OTP 19.0 起)
-type state_name() :: atom().

回呼模式 state_functions 中的狀態名稱。

如果 回呼模式state_functions,則狀態必須為原子。在狀態變更 ( NextState =/= State ) 之後,所有延遲的事件都會重試。請注意,狀態 terminate 無法使用,因為它會與選用的回呼函式 Module:terminate/3 衝突。

連結到此類型

state_timeout()

檢視原始碼 (未匯出) (自 OTP 19.3 起)
-type state_timeout() :: Time :: timeout() | integer().

在目前狀態中等待的時間長度。

啟動由 timeout_action/0{state_timeout, Time, EventContent [, Options]} 設定的計時器。

當計時器逾時時,會產生一個 event_type/0state_timeout 事件。關於如何解釋 TimeOptions,請參閱 erlang:start_timer/4。未來不一定會支援 erlang:start_timer/4Options

如果計時器正在執行,狀態變更會取消這個計時器。也就是說,如果啟動此計時器的 timeout_action/0狀態變更action/0 列表的一部分,NextState =/= CurrentState,則計時器會在 NextState 中執行。

如果狀態機停留在新的狀態(現在是目前狀態),計時器將會執行直到逾時,並產生逾時事件。如果狀態機從現在的目前狀態變更狀態,則會取消計時器。在從現在的目前狀態變更狀態期間,可能會為下一個 NextState 啟動新的狀態逾時

如果啟動此計時器的 timeout_action/0 是非狀態變更狀態轉換action/0 列表的一部分,則計時器會在目前狀態中執行。

如果 Timeinfinity,則不會啟動計時器,因為它永遠不會到期。

如果 Time 是相對時間且為 0,則實際上不會啟動計時器,而是將逾時事件加入佇列,以確保它在任何尚未收到的外部事件之前被處理。

如果在計時器執行時設定此計時器,則會使用新的逾時值重新啟動它。因此,可以透過將其設定為 infinity 來取消此逾時。也可以使用 {state_timeout, cancel} 更明確地取消它。

計時器的 EventContent 可以使用 {state_timeout, update, NewEventContent} 動作來更新,而不會影響逾時時間。

連結到此類型

timeout_action()

檢視原始碼 (未匯出) (自 OTP 19.0 起)
-type timeout_action() ::
          (Time :: event_timeout()) |
          {timeout, Time :: event_timeout(), EventContent :: event_content()} |
          {timeout,
           Time :: event_timeout(),
           EventContent :: event_content(),
           Options :: timeout_option() | [timeout_option()]} |
          {{timeout, Name :: term()}, Time :: generic_timeout(), EventContent :: event_content()} |
          {{timeout, Name :: term()},
           Time :: generic_timeout(),
           EventContent :: event_content(),
           Options :: timeout_option() | [timeout_option()]} |
          {state_timeout, Time :: state_timeout(), EventContent :: event_content()} |
          {state_timeout,
           Time :: state_timeout(),
           EventContent :: event_content(),
           Options :: timeout_option() | [timeout_option()]} |
          timeout_cancel_action() |
          timeout_update_action().

事件逾時、通用逾時或狀態逾時。

這些轉換動作可以透過從 狀態回呼、從 Module:init/1 或將它們傳遞給 enter_loop/4,5,6 來調用。

這些逾時動作會設定逾時 轉換選項

  • Time - {timeout, Time, Time} 的縮寫,也就是說,逾時訊息是逾時時間。此形式的存在是為了允許 狀態回呼 傳回值 {next_state, NextState, NewData, Time},就像在 gen_fsm 中一樣。

  • {timeout, Time, EventContent [, Options]} - 將 transition_option/0 event_timeout/0 設定為 Time,並帶有 EventContent 和逾時選項 Options

  • {{timeout,Name}, Time, EventContent [, Options]} - 將 transition_option/0 generic_timeout/0 設定為逾時 NameTime,並帶有 EventContent 和逾時選項 Options
    自 OTP 20.0 起.

  • {state_timeout, Time, EventContent [, Options]} - 將 transition_option/0 state_timeout/0 設定為 Time,並帶有 EventContent 和逾時選項 Options
    自 OTP 19.3 起.

連結到此類型

timeout_cancel_action()

檢視原始碼 (未匯出) (自 OTP 22.1 起)
-type timeout_cancel_action() ::
          {timeout, cancel} | {{timeout, Name :: term()}, cancel} | {state_timeout, cancel}.

比原始設定為「無限」更明確的取消逾時方式。

一直以來,都可以使用 timeout_action/0Time = infinity 來取消逾時,因為設定新的逾時時間會覆蓋正在執行的計時器,而且將時間設定為 infinity 會被最佳化為不設定計時器(永遠不會逾時)。使用此動作可以更清楚地顯示意圖。

連結到此類型

timeout_event_type()

檢視原始碼 (未匯出) (自 OTP 19.0 起)
-type timeout_event_type() :: timeout | {timeout, Name :: term()} | state_timeout.

事件逾時通用逾時狀態逾時

狀態機可以使用對應的 timeout_action/0 為自己產生的逾時事件類型

逾時類型動作事件類型
事件逾時{timeout, Time, ...}timeout
通用逾時{{timeout, Name}, Time, ...}{timeout, Name}
狀態逾時{state_timeout, Time, ...}state_timeout

簡而言之;設定具有 EventType 的逾時的動作是 {EventType, Time, ...}

連結到此類型

timeout_option()

檢視原始碼 (未匯出) (自 OTP 19.0 起)
-type timeout_option() :: {abs, Abs :: boolean()}.

逾時計時器啟動選項,可選擇絕對的到期時間。

如果 Abstrue,則會啟動絕對計時器;如果為 false,則會啟動相對計時器,這是預設值。有關詳細資訊,請參閱 erlang:start_timer/4

連結到此類型

timeout_update_action()

檢視原始碼 (未匯出) (自 OTP 22.1 起)
-type timeout_update_action() ::
          {timeout, update, EventContent :: event_content()} |
          {{timeout, Name :: term()}, update, EventContent :: event_content()} |
          {state_timeout, update, EventContent :: event_content()}.

更新 EventContent,而不影響到期時間。

為正在執行的逾時計時器設定新的 EventContent。關於如何啟動逾時,請參閱 timeout_action()

如果沒有此類型的逾時處於活動狀態,則會像使用相對 Time = 0 啟動逾時一樣,插入逾時事件。這是一個具有立即逾時的逾時自動啟動,因此,如果例如通用逾時名稱拼寫錯誤,則會產生雜訊。

連結到此類型

transition_option()

檢視原始碼 (自 OTP 19.0 起)
-type transition_option() ::
          postpone() | hibernate() | event_timeout() | generic_timeout() | state_timeout().

動作設定的狀態轉換選項。

這些決定在狀態轉換期間會發生什麼。當 狀態回呼 處理完事件並傳回時,就會發生狀態轉換。以下是狀態轉換的步驟順序

  1. 所有傳回的 動作 都會按照出現順序處理。在此步驟中,會傳送由任何 reply_action/0 產生的所有回覆。其他動作會設定 transition_option/0,這些選項會在後續步驟中生效。

  2. 如果使用 狀態進入呼叫,則會使用初始狀態或其中一個回呼結果 repeat_staterepeat_state_and_data,則 gen_statem 引擎會使用引數 (enter, State, Data)(enter, State, State, Data)(取決於 回呼模式)呼叫目前的狀態回呼,當它傳回時,會再次從此序列的頂端開始。

    如果使用 狀態進入呼叫,並且狀態變更,則 gen_statem 引擎會使用引數 (enter, OldState, Data)(enter, OldState, State, Data)(取決於 回呼模式)呼叫新的狀態回呼,當它傳回時,會再次從此序列的頂端開始。

  3. 如果 postpone/0true,則會延後目前的事件。

  4. 如果這是狀態變更,則會重設傳入事件的佇列,從最舊的延後事件開始。

  5. 所有透過 action/0 next_event 儲存的事件都會插入,以便在先前加入佇列的事件之前處理。

  6. 會處理逾時計時器 event_timeout/0generic_timeout/0state_timeout/0。保證會將零時間逾時傳遞給狀態機,在任何尚未收到的外部事件之前,因此,如果請求了此類逾時,則會將對應的逾時零事件加入佇列作為最新的已接收事件;也就是說,在已加入佇列的事件(例如插入和延後的事件)之後。

    任何事件都會取消 event_timeout/0,因此只有在事件佇列為空時,才會產生零時間事件逾時。

    狀態變更會取消 state_timeout/0,並且此類型的任何新的轉換選項都屬於新狀態,也就是說,state_timeout/0 適用於狀態機進入的狀態。

  7. 如果有已加入佇列的事件,則會使用最舊的已加入佇列事件呼叫可能的新狀態的 狀態回呼,並且我們會再次從此序列的頂端開始。

  8. 否則,gen_statem 會進入 receive 或休眠狀態(如果 hibernate/0true),以等待下一個訊息。在休眠狀態下,下一個非系統事件會喚醒 gen_statem,或者更確切地說,下一個傳入訊息會喚醒 gen_statem,但如果它是系統事件,則會立即返回休眠狀態。當新的訊息到達時,會使用對應的事件呼叫 狀態回呼,並且我們會再次從此序列的頂端開始。

注意

零時間逾時(時間為 0 的逾時)的行為與 Erlang 的 receive ... after 0 ... end 略有不同。

後者若有訊息則接收一個訊息,而使用 timeout_action/0 {timeout, 0} 則不會接收任何外部事件。

gen_server 的逾時機制如同 Erlang 的 receive ... after 0 ... end,與 gen_statem 的機制相反。

回呼函式

連結至此回呼函式

callback_mode()

檢視原始碼 (自 OTP 19.1 起)
-callback callback_mode() -> callback_mode_result().

選取回呼模式,以及可能的 狀態進入呼叫

gen_statem 需要找出回呼模組的回呼模式時,會呼叫此函式。

為了效率考量,此值會由 gen_statem 進行快取,因此此函式只會在伺服器啟動後、程式碼變更後,以及變更回呼模組後,並且是在呼叫目前回呼模組程式碼中的第一個狀態回呼函式之前,才會呼叫一次。在未來版本的 gen_statem 中可能會增加更多呼叫時機。

伺服器啟動發生在 Module:init/1 回傳時,或在呼叫 enter_loop/4,5,6 時。程式碼變更發生在 Module:code_change/4 回傳時。回呼模組的變更發生在狀態回呼函式回傳任何動作 change_callback_modulepush_callback_modulepop_callback_module 時。

CallbackMode 可以是 callback_mode/0 本身,或是一個包含 callback_mode/0 以及可能包含原子 state_enter 的列表。

注意

如果此函式的主體沒有回傳一個內嵌常數值,則表示回呼模組正在執行一些奇怪的操作。

連結至此回呼函式

code_change(OldVsn, OldState, OldData, Extra)

檢視原始碼 (可選) (自 OTP 19.0 起)
-callback code_change(OldVsn :: term() | {down, term()},
                      OldState :: state(),
                      OldData :: data(),
                      Extra :: term()) ->
                         {ok, NewState :: state(), NewData :: data()} | (Reason :: term()).

在程式碼變更後更新 statedata

gen_statem 要在發佈升級/降級期間更新其內部狀態時,會呼叫此函式,也就是當 appup 檔案中指定了指令 {update, Module, Change, ...},其中 Change = {advanced, Extra} 時。如需詳細資訊,請參閱 OTP 設計原則

對於升級,OldVsnVsn,而對於降級,OldVsn{down, Vsn}Vsn 由舊版本的回呼模組 Modulevsn 屬性定義。如果沒有定義這樣的屬性,則版本是 Beam 檔案的校驗和。

OldStateOldDatagen_statem 的內部狀態。

Extra 從更新指令的 {advanced, Extra} 部分「原樣」傳遞。

如果成功,函式必須在 {ok, NewState, NewData} 元組中回傳更新後的內部狀態。

如果函式回傳失敗 Reason,則正在進行的升級會失敗並回滾到舊版本。請注意,Reason 不能是 {ok, _, _} 元組,因為該元組會被視為 {ok, NewState, NewData} 元組,而且符合 {ok, _} 的元組也是無效的失敗 Reason。建議使用原子作為 Reason,因為它會被包裝在 {error, Reason} 元組中。

另請注意,在升級 gen_statem 時,此函式以及 appup 檔案中的 Change = {advanced, Extra} 參數不僅僅需要更新內部狀態或對 Extra 參數執行操作。如果升級或降級應該變更回呼模式,也需要它,否則程式碼變更後的回呼模式將不會被採用,很可能會導致伺服器崩潰。

如果伺服器使用任何動作 change_callback_modulepush_callback_modulepop_callback_module 來變更回呼模組,請注意,始終是目前的回呼模組會收到此回呼呼叫。目前的回呼模組處理目前的狀態和資料應該不足為奇,但它必須能夠處理甚至是不熟悉的狀態和資料的部分。

在監督程式的子規格中,有一個模組列表,建議只包含回呼模組。對於具有多個回呼模組的 gen_statem,沒有真正需要列出所有模組的必要,甚至可能無法做到,因為該列表可能會在程式碼升級後變更。如果此列表僅包含啟動回呼模組,如建議的那樣,重要的是在執行同步程式碼替換時升級模組。然後,發佈處理常式會得出結論,升級模組的升級需要暫停、變更程式碼並恢復任何子規格宣告使用模組的伺服器。再次說明,目前的回呼模組將收到 Module:code_change/4 呼叫。

注意

如果在 .appup 檔案中指定了 Change = {advanced, Extra} 的發佈升級/降級時,而沒有實作 Module:code_change/4,則程序會因結束原因 undef 而崩潰。

連結至此回呼函式

format_status(Status)

檢視原始碼 (可選) (自 OTP 25.0 起)
-callback format_status(Status) -> NewStatus when Status :: format_status(), NewStatus :: format_status().

格式化/限制狀態值。

為了除錯和記錄目的,gen_statem 程序會呼叫此函式來格式化/限制伺服器狀態。

它會在以下情況下被呼叫

  • 呼叫 sys:get_status/1,2 以取得 gen_statem 狀態。
  • gen_statem 程序異常終止並記錄錯誤。

此函式對於變更這些情況下 gen_statem 狀態的形式和外觀很有用。希望變更 sys:get_status/1,2 回傳值以及其狀態在終止錯誤記錄中顯示方式的回呼模組,應匯出 Module:format_status/1 的實例,該實例將取得一個描述 gen_statem 目前狀態的地圖 Status,並且應回傳一個包含與輸入地圖相同鍵的地圖 NewStatus,但可能會轉換某些值。

此函式的一個用例是回傳緊湊的替代狀態表示法,以避免在記錄檔中印出大型狀態項。另一個用例是隱藏敏感資料,使其不被寫入錯誤記錄。

範例

format_status(Status) ->
  maps:map(
    fun(state,State) ->
            maps:remove(private_key, State);
       (message,{password, _Pass}) ->
            {password, removed};
       (_,Value) ->
            Value
    end, Status).

注意

此回呼是可選的,因此回呼模組不需要匯出它。gen_statem 模組提供此函式的預設實作,該實作會回傳 {State, Data}

如果此回呼函式被匯出但失敗,為了隱藏可能敏感的資料,預設函式將改為回傳 {State, Info},其中 Info 除了說明 Module:format_status/2 崩潰之外,沒有其他資訊。

連結至此回呼函式

format_status/2

檢視原始碼 (可選) (自 OTP 19.0 起)
此回呼已棄用。回呼 gen_statem:format_status(_,_) 已棄用;請改用 format_status/1。
-callback format_status(StatusOption, [[{Key :: term(), Value :: term()}] | state() | data()]) ->
                           Status :: term()
                           when StatusOption :: normal | terminate.

格式化/限制狀態值。

為了除錯和記錄目的,gen_statem 程序會呼叫此函式來格式化/限制伺服器狀態。

它會在以下情況下被呼叫

  • 呼叫 sys:get_status/1,2 其中之一來取得 gen_statem 狀態。在這種情況下,Opt 會設定為原子 normal

  • gen_statem 異常終止並記錄錯誤。在這種情況下,Opt 會設定為原子 terminate

此函式對於變更這些情況下 gen_statem 狀態的形式和外觀很有用。希望變更 sys:get_status/1,2 回傳值以及其狀態在終止錯誤記錄中顯示方式的回呼模組,應匯出 Module:format_status/2 的實例,該實例會回傳一個描述 gen_statem 目前狀態的詞彙。

PDictgen_statem 的程序字典的目前值。

Stategen_statem 的內部狀態。

Datagen_statem 的內部伺服器資料。

此函式會回傳 Status,一個包含 gen_statem 目前狀態和狀況的適當詳細資訊的詞彙。Status 可以採用的形式沒有任何限制,但是對於 sys:get_status/1,2 的情況 (當 Optnormal 時),Status 值的建議形式為 [{data, [{"State", Term}]}],其中 Term 提供 gen_statem 狀態的相關詳細資訊。遵循此建議不是必須的,但它會使回呼模組狀態與其餘的 sys:get_status/1,2 回傳值保持一致。

此函式的一個用途是回傳緊湊的替代狀態表示法,以避免在記錄檔中印出大型狀態項。另一個用途是隱藏敏感資料,使其不被寫入錯誤記錄。

注意

此回呼是可選的,因此回呼模組不需要匯出它。gen_statem 模組提供此函式的預設實作,該實作會回傳 {State, Data}

如果此回呼函式被匯出但失敗,為了隱藏可能敏感的資料,預設函式將改為回傳 {State, Info},其中 Info 除了說明 Module:format_status/2 崩潰之外,沒有其他資訊。

連結至此回呼函式

handle_event/4

檢視原始碼 (可選) (自 OTP 19.0 起)
-callback handle_event(enter, OldState, CurrentState, Data) -> state_enter_result(CurrentState)
                          when OldState :: state(), CurrentState :: state(), Data :: data();
                      (EventType, EventContent, CurrentState, Data) -> event_handler_result(state())
                          when
                              EventType :: event_type(),
                              EventContent :: event_content(),
                              CurrentState :: state(),
                              Data :: data().

回呼模式 handle_event_function 中的狀態回呼

每當 gen_statemcall/2,3cast/2 或作為一般程序訊息接收到事件時,都會呼叫此函式。

如果 EventType{call, From},則呼叫者會等待回覆。可以從此函式或從任何其他狀態回呼函式傳送回覆,方法是在 ActionsReplies 中回傳 {reply, From, Reply},或呼叫 reply(From, Reply)

如果此函式回傳的下一個狀態與目前狀態不相等 (=/=),則所有延遲的事件都會在下一個狀態中重試。

對於可以設定的選項以及 gen_statem 從此函式回傳後可以執行的動作,請參閱 action/0

gen_statem 執行時,使用 狀態進入呼叫,此函數也會在每次狀態變更期間以引數 (enter, OldState, ...) 呼叫。 在這種情況下,對於可能返回的 動作有一些限制

  • 不允許使用 postpone/0,因為狀態進入呼叫不是事件,因此沒有事件可以延後。

  • 不允許使用 {next_event, _, _},因為使用狀態進入呼叫不應影響事件的消耗和產生方式。

  • 不允許從此呼叫變更狀態。如果您返回 {next_state, NextState, ...}NextState =/= State,則 gen_statem 會崩潰。

    請注意,實際上允許使用 {repeat_state, NewData, ...},儘管它沒有什麼意義,因為您會立即再次以新的狀態進入呼叫被呼叫,這只是一種奇怪的循環方式,而且在 Erlang 中有更好的循環方式。

    如果您不更新 NewData 並且有一些循環終止條件,或者如果您使用 {repeat_state_and_data, _}repeat_state_and_data,您將會陷入無限循環!

    建議您使用 {keep_state, ...}{keep_state_and_data, _}keep_state_and_data,因為無論如何都不可能從狀態進入呼叫變更狀態。

請注意,您可以使用 throw 來返回結果,這可能很有用。 例如,從無法返回 {next_state, State, Data} 的複雜程式碼深處使用 throw(keep_state_and_data) 退出,因為 StateData 不再在範圍內。

連結至此回呼函式

init(Args)

檢視原始碼 (自 OTP 19.0 起)
-callback init(Args :: term()) -> init_result(state()).

初始化狀態機器。

每當使用 start_link/3,4start_monitor/3,4start/3,4 啟動 gen_statem 時,新進程會呼叫此函數來初始化實作狀態和伺服器資料。

Args 是提供給該啟動函數的 Args 引數。

注意

請注意,如果 gen_statem 是透過 proc_libenter_loop/4,5,6 啟動的,則永遠不會呼叫此回呼。由於此回呼並非可選,因此在這種情況下,它可以實作為

-spec init(_) -> no_return().
init(Args) -> erlang:error(not_implemented, [Args]).
連結至此回呼函式

'StateName'/3

檢視原始碼 (可選) (自 OTP 19.0 起)
-callback 'StateName'(enter, OldStateName :: state_name(), data()) -> state_enter_result(state_name);
                     (EventType :: event_type(), EventContent :: event_content(), Data :: data()) ->
                         event_handler_result(state_name()).

回呼模式 state_functions 中的狀態回呼

狀態回呼,處理狀態 StateName 中的所有事件,其中 StateName :: state_name() 必須是 atom/0

StateName 不能是 terminate,因為這會與回呼函數 Module:terminate/3 衝突。

除此之外,當進行 狀態變更 時,下一個狀態必須始終是 atom/0,此函數等效於 Module:handle_event(​EventType, EventContent, ?FUNCTION_NAME, Data),這是 回呼模式 handle_event_function 中的 狀態回呼

連結至此回呼函式

terminate/3

檢視原始碼 (可選) (自 OTP 19.0 起)
-callback terminate(Reason :: normal | shutdown | {shutdown, term()} | term(),
                    CurrentState :: state(),
                    data()) ->
                       any().

處理狀態機器終止。

gen_statem 即將終止時,會呼叫此函數。它應與 Module:init/1 相反,並執行任何必要的清理。當它返回時,gen_statem 會以 Reason 終止。會忽略傳回值。

Reason 是一個表示停止原因的詞彙,而 Stategen_statem 的內部狀態。

Reason 取決於 gen_statem 終止的原因。如果是因為另一個回呼函數已在 Actions 中傳回停止元組 {stop, Reason},則 Reason 具有該元組中指定的值。如果是因為失敗,則 Reason 是錯誤原因。

如果 gen_statem 是監督樹的一部分,並由其監督者命令終止,則當以下兩個條件都適用時,會以 Reason = shutdown 呼叫此函數

  • 已將 gen_statem 進程設定為捕獲結束訊號。
  • 監督者子規範中定義的關閉策略是一個整數逾時值,而不是 brutal_kill

即使 gen_statem 不是監督樹的一部分,如果它收到來自其父系的 'EXIT' 訊息,也會呼叫此函數。Reason'EXIT' 訊息中的相同。

如果 gen_statem 進程未設定為捕獲結束訊號,它會立即終止,就像任何進程一樣,並且不會呼叫此函數。

請注意,對於任何其他原因,而非 normalshutdown{shutdown, Term},則會假定 gen_statem 是因錯誤而終止,並且會使用 logger 發出錯誤報告。

gen_statem 進程退出時,會向連結的進程和埠傳送具有相同原因的結束訊號,就像任何進程一樣。

函數

此函數的連結

call(ServerRef, Request)

檢視原始碼 (自 OTP 19.0 起)
-spec call(ServerRef :: server_ref(), Request :: term()) -> Reply :: term().

等同於 call(ServerRef, Request, infinity)

此函數的連結

call(ServerRef, Request, Timeout)

檢視原始碼 (自 OTP 19.0 起)
-spec call(ServerRef :: server_ref(),
           Request :: term(),
           Timeout :: timeout() | {clean_timeout, T :: timeout()} | {dirty_timeout, T :: timeout()}) ->
              Reply :: term().

呼叫伺服器:傳送請求並等待回應。

透過傳送請求並等待回應到達,對 gen_statem ServerRef 進行同步呼叫。

gen_statem 使用 event_type/0 {call, From} 和事件內容 Request 呼叫 狀態回呼

伺服器的回覆是從 狀態回呼 傳送的,方法是傳回 轉換動作 {reply, From, Reply},使用 Replies 列表中的此類回覆動作呼叫 reply(Replies),或呼叫 reply(From, Reply)

Timeout 是一個整數 > 0,指定等待回覆的毫秒數,或原子 infinity 以無限期等待,這是預設值。如果在指定的時間內未收到回覆,則函數呼叫失敗。

當發生網路問題或使用 dirty_timeout 時可能發生的先前延遲回覆問題現在已透過使用 進程別名 來避免。 因此,{clean_timeout, T}{dirty_timeout, T} 不再有任何用途,並且將與 Timeout 的作用相同,同時它們也同樣有效。

呼叫也可能會失敗,例如,如果 gen_statem 在此函數呼叫之前或期間死亡。

當此呼叫失敗時,它會 退出 呼叫進程。退出詞彙的形式為 {Reason, Location},其中 Location = {gen_statem, call, ArgList}。請參閱 gen_server:call/3,其中說明了退出詞彙中 Reason 的相關值。

此函數的連結

cast(ServerRef, Msg)

檢視原始碼 (自 OTP 19.0 起)
-spec cast(ServerRef :: server_ref(), Msg :: term()) -> ok.

將事件發送至伺服器。

將非同步 cast 事件傳送到 gen_statem ServerRef,並立即傳回 ok,忽略目的地節點或 gen_statem 是否存在。

gen_statem 使用 event_type/0 cast 和事件內容 Msg 呼叫 狀態回呼

此函數的連結

check_response(Msg, ReqId)

檢視原始碼 (自 OTP 23.0 起)
-spec check_response(Msg, ReqId) -> Result
                        when
                            Msg :: term(),
                            ReqId :: request_id(),
                            Response ::
                                {reply, Reply :: term()} | {error, {Reason :: term(), server_ref()}},
                            Result :: Response | no_reply.

檢查收到的訊息是否為請求回應。

檢查 Msg 是否是對應於請求識別碼 ReqId 的回應。請求必須由 send_request/2 和呼叫此函數的相同進程發出。

如果 Msg 是對控制代碼 ReqId 的回覆,則請求的結果會以 Reply 傳回。否則,此函數會傳回 no_reply,且不會執行清理,因此應重複調用該函數,直到傳回回應為止。

請參閱 call/3,了解如何處理請求以及 gen_statem 伺服器如何傳送 Reply

如果在呼叫此函數時 gen_statem 伺服器進程已死,也就是說,Msg 會報告伺服器已死,則此函數會傳回具有退出 Reasonerror 傳回。

此函數的連結

check_response(Msg, ReqIdCollection, Delete)

檢視原始碼 (自 OTP 25.0 起)
-spec check_response(Msg, ReqIdCollection, Delete) -> Result
                        when
                            Msg :: term(),
                            ReqIdCollection :: request_id_collection(),
                            Delete :: boolean(),
                            Response ::
                                {reply, Reply :: term()} | {error, {Reason :: term(), server_ref()}},
                            Result ::
                                {Response,
                                 Label :: term(),
                                 NewReqIdCollection :: request_id_collection()} |
                                no_request | no_reply.

檢查收到的訊息是否為集合中的請求回應。

檢查 Msg 是否是對應於儲存在 ReqIdCollection 中的請求識別碼的回應。ReqIdCollection 的所有請求識別碼都必須對應於由呼叫此函數的進程使用 send_request/2send_request/4 發出的請求。

回應中的 Label 等於與該回應對應的請求識別碼相關聯的 Label。請求識別碼的 Label 會在將請求 ID 儲存至集合時,或使用 send_request/4 發送請求時關聯。

相較於 check_response/2,與特定請求識別碼相關的回傳結果或例外狀況將會被包裝在一個 3 元組 {Response, Label, NewReqIdCollection} 中。Responsecheck_response/2 會產生的值,Label 是與特定 請求識別碼 相關聯的值,而 NewReqIdCollection 則是一個可能已修改的請求識別碼集合。

如果 ReqIdCollection 為空,則會回傳 no_request

如果 Msg 不對應到 ReqIdCollection 中的任何請求識別碼,則會回傳 no_reply

如果 Delete 等於 true,則與 Label 的關聯已從結果的 NewReqIdCollection 中的 ReqIdCollection 刪除。如果 Deletefalse,則 NewReqIdCollection 將會等於 ReqIdCollection。請注意,刪除關聯並非免費,並且包含已處理請求的集合仍可由後續對 wait_response/3check_response/3receive_response/3 的呼叫使用。

然而,如果不刪除已處理的關聯,上述呼叫將無法偵測何時沒有更多未處理的請求要處理,因此您必須以其他方式追蹤此狀況,而不是依賴 no_request 回傳值。請注意,如果您將一個僅包含已處理或已放棄請求的關聯的集合傳遞給此函式,它將始終回傳 no_reply

此函數的連結

enter_loop(Module, Opts, State, Data)

檢視原始碼 (自 OTP 19.1 起)
-spec enter_loop(Module :: term(), Opts :: term(), State :: term(), Data :: term()) -> no_return().

等同於 enter_loop(Module, Opts, State, Data, self(), [])

此函數的連結

enter_loop(Module, Opts, State, Data, Server_or_Actions)

檢視原始碼 (自 OTP 19.0 起)
-spec enter_loop(Module :: term(), Opts :: term(), State :: term(), Data :: term(), Actions) ->
                    no_return()
                    when Actions :: list();
                (Module :: term(), Opts :: term(), State :: term(), Data :: term(), Server) ->
                    no_return()
                    when Server :: server_name() | pid().

使呼叫程序成為 gen_statem 伺服器。

使用引數 Actions,等同於 enter_loop(Module, Opts, State, Data, self(), Actions)

否則等同於 enter_loop(Module, Opts, State, Data, Server, [])

此函數的連結

enter_loop(Module, Opts, State, Data, Server, Actions)

檢視原始碼 (自 OTP 19.0 起)
-spec enter_loop(Module :: module(),
                 Opts :: [enter_loop_opt()],
                 State :: state(),
                 Data :: data(),
                 Server :: server_name() | pid(),
                 Actions :: [action()] | action()) ->
                    no_return().

使呼叫程序成為 gen_statem 伺服器。

不回傳任何值,而是呼叫的處理程序會進入 gen_statem 接收迴圈並成為 gen_statem 伺服器。該處理程序必須已使用 proc_lib 中的其中一個啟動函式啟動。使用者負責處理該處理程序的任何初始化,包括為其註冊名稱。

當需要的初始化程序比 gen_statem Module:init/1 回呼提供的更複雜時,此函式會很有用。

ModuleOpts 的含義與呼叫 start[link | monitor]/3,4 時相同。

如果 Serverself/0,則會建立一個匿名伺服器,就像使用 start[link |_monitor]/3 時一樣。如果 Serverserver_name/0,則會建立一個具名伺服器,就像使用 start[link |_monitor]/4 時一樣。然而,server_name/0 名稱必須在呼叫此函式之前依據進行註冊。

StateDataActions 的含義與 Module:init/1 的回傳值中的含義相同。此外,回呼模組不需要匯出 Module:init/1 函式。

如果呼叫的處理程序不是由 proc_lib 啟動函式啟動,或者如果它沒有按照 server_name/0 註冊,則此函式會失敗。

此函數的連結

receive_response(ReqId)

檢視原始碼 (自 OTP 24.0 起)
-spec receive_response(ReqId) -> Result
                          when
                              ReqId :: request_id(),
                              Response ::
                                  {reply, Reply :: term()} | {error, {Reason :: term(), server_ref()}},
                              Result :: Response | timeout.

等同於 receive_response(ReqId, infinity)

此函數的連結

receive_response(ReqId, Timeout)

檢視原始碼 (自 OTP 24.0 起)
-spec receive_response(ReqId, Timeout) -> Result
                          when
                              ReqId :: request_id(),
                              Timeout :: response_timeout(),
                              Response ::
                                  {reply, Reply :: term()} | {error, {Reason :: term(), server_ref()}},
                              Result :: Response | timeout.

接收請求回應。

接收對應於請求識別碼 ReqId 的回應。請求必須已由 send_request/2gen_statem 處理程序提出。此函式必須從提出 send_request/2 的同一個處理程序呼叫。

Timeout 指定等待回應的時間長度。如果在指定的時間內沒有收到回應,則此函式會回傳 timeout。假設伺服器在支援別名的節點上執行(在 OTP 24 中引入),則請求也會被放棄。也就是說,在逾時後將不會收到任何回應。否則,可能會在稍後收到異常回應。

請參閱 call/3,了解如何處理請求以及 gen_statem 伺服器如何傳送 Reply

如果在等待回覆時 gen_statem 伺服器處理程序已死或死亡,則會回傳一個包含結束 Reasonerror 回傳值。

wait_response/2receive_response/2 之間的差異在於,receive_response/2 會在逾時時放棄請求,因此會忽略潛在的未來回應,而 wait_response/2 則不會。

此函數的連結

receive_response(ReqIdCollection, Timeout, Delete)

檢視原始碼 (自 OTP 25.0 起)
-spec receive_response(ReqIdCollection, Timeout, Delete) -> Result
                          when
                              ReqIdCollection :: request_id_collection(),
                              Timeout :: response_timeout(),
                              Delete :: boolean(),
                              Response ::
                                  {reply, Reply :: term()} | {error, {Reason :: term(), server_ref()}},
                              Result ::
                                  {Response,
                                   Label :: term(),
                                   NewReqIdCollection :: request_id_collection()} |
                                  no_request | timeout.

接收集合中的請求回應。

ReqIdCollection 中接收回應。ReqIdCollection 的所有請求識別碼都必須對應於使用 send_request/2send_request/4 提出的請求,並且所有請求都必須由呼叫此函式的處理程序提出。

回應中的 Label 是與該回應對應的請求識別碼相關聯的 Label。請求識別碼的 Label 會在將請求 ID 新增至集合時,或使用 send_request/4 發送請求時關聯。

相較於 receive_response/2,與特定請求識別碼相關的回傳結果或例外狀況將會被包裝在一個 3 元組 {Response, Label, NewReqIdCollection} 中。Responsereceive_response/2 會產生的值,Label 是與特定 請求識別碼 相關聯的值,而 NewReqIdCollection 則是一個可能已修改的請求識別碼集合。

如果 ReqIdCollection 為空,則會回傳 no_request

Timeout 指定等待回應的時間長度。如果在指定的時間內沒有收到回應,則此函式會回傳 timeout。假設伺服器在支援別名的節點上執行(在 OTP 24 中引入),則 ReqIdCollection 所識別的所有請求也會被放棄。也就是說,在逾時後將不會收到任何回應。否則,可能會在稍後收到異常回應。

receive_response/3wait_response/3 之間的差異在於,receive_response/3 會在逾時時放棄請求,因此會忽略潛在的未來回應,而 wait_response/3 則不會。

如果 Deletetrue,則與 Label 的關聯會從結果的 NewReqIdCollection 中的 ReqIdCollection 刪除。如果 Deletefalse,則 NewReqIdCollection 將會等於 ReqIdCollection。請注意,刪除關聯並非免費,並且包含已處理請求的集合仍可由後續對 wait_response/3check_response/3receive_response/3 的呼叫使用。

然而,如果不刪除已處理的關聯,上述呼叫將無法偵測何時沒有更多未處理的請求要處理,因此您必須以其他方式追蹤此狀況,而不是依賴 no_request 回傳值。請注意,如果您將一個僅包含已處理或已放棄請求的關聯的集合傳遞給此函式,它將始終封鎖直到 Timeout 過期,然後回傳 timeout

此函數的連結

reply(Replies)

檢視原始碼 (自 OTP 19.0 起)
-spec reply(Replies :: [reply_action()] | reply_action()) -> ok.

傳送一個或多個 call 回覆。

此函數可被 gen_statem 回呼函數使用,以明確地將一個或多個回覆傳送給正在等待 call 請求回覆的程序,當從狀態回呼函數返回 reply_action/0 不切實際或不可能時。

注意

使用此函數傳送的回覆在 sys 偵錯輸出中不可見。

此函數的連結

reply(From, Reply)

檢視原始碼 (自 OTP 19.0 起)
-spec reply(From :: from(), Reply :: term()) -> ok.

傳送 call ReplyFrom

此函數可被 gen_statem 回呼函數使用,以明確地將回覆傳送給正在等待 call 請求回覆的程序,當從狀態回呼函數返回 reply_action/0 不切實際或不可能時。

注意

使用此函數傳送的回覆在 sys 偵錯輸出中不可見。

此函數的連結

reqids_add(ReqId, Label, ReqIdCollection)

檢視原始碼 (自 OTP 25.0 起)
-spec reqids_add(ReqId :: request_id(), Label :: term(), ReqIdCollection :: request_id_collection()) ->
                    NewReqIdCollection :: request_id_collection().

在集合中儲存請求識別碼。

儲存 ReqId 並將 Label 與請求識別碼關聯,方法是將此資訊新增到 ReqIdCollection 並傳回產生的請求識別碼集合。

此函數的連結

reqids_new()

檢視原始碼 (自 OTP 25.0 起)
-spec reqids_new() -> NewReqIdCollection :: request_id_collection().

建立一個空的請求識別碼集合。

傳回一個新的空請求識別碼集合。請求識別碼集合可用於處理多個未完成的請求。

send_request/2 發出的請求的請求識別碼可以使用 reqids_add/3 儲存在集合中。稍後可以透過將集合作為引數傳遞給 receive_response/3wait_response/3check_response/3,從此請求識別碼集合中取得對應於請求的回覆。

reqids_size/1 可用於判斷集合中請求識別碼的數量。

此函數的連結

reqids_size(ReqIdCollection)

檢視原始碼 (自 OTP 25.0 起)
-spec reqids_size(ReqIdCollection :: request_id_collection()) -> non_neg_integer().

返回 ReqIdCollection 中請求識別碼的數量。

此函數的連結

reqids_to_list(ReqIdCollection)

檢視原始碼 (自 OTP 25.0 起)
-spec reqids_to_list(ReqIdCollection :: request_id_collection()) ->
                        [{ReqId :: request_id(), Label :: term()}].

將請求識別碼集合轉換為列表。

傳回一個 {ReqId, Label} 元組的列表,該列表對應於 ReqIdCollection 中所有帶有相關標籤的請求識別碼。

此函數的連結

send_request(ServerRef, Request)

檢視原始碼 (自 OTP 23.0 起)
-spec send_request(ServerRef :: server_ref(), Request :: term()) -> ReqId :: request_id().

傳送一個非同步的 call 請求。

Request 傳送至 ServerRef 所識別的 gen_statem 程序,並傳回請求識別碼 ReqId

傳回值 ReqId 稍後應與 receive_response/2wait_response/2check_response/2 一起使用,以提取請求的實際結果。除了直接將請求識別碼傳遞給這些函數之外,它還可以使用 reqids_add/3 儲存在請求識別碼集合中。稍後可以透過將集合作為引數傳遞給 receive_response/3wait_response/3check_response/3,從此請求識別碼集合中取得對應於請求的回覆。如果您打算將請求識別碼儲存在集合中,您可能需要考慮改用 send_request/4

呼叫 gen_statem:wait_response(gen_statem:send_request(ServerRef, Request), Timeout) 可以視為等同於 gen_statem:call(Server, Request, Timeout),忽略錯誤處理。

請參閱 call/3,了解如何處理請求以及 gen_statem 伺服器如何傳送 Reply

伺服器的 Replyreceive_response/1,2wait_response/1,2check_response/2 函數之一傳回。

此函數的連結

send_request(ServerRef, Request, Label, ReqIdCollection)

檢視原始碼 (自 OTP 25.0 起)
-spec send_request(ServerRef :: server_ref(),
                   Request :: term(),
                   Label :: term(),
                   ReqIdCollection :: request_id_collection()) ->
                      NewReqIdCollection :: request_id_collection().

傳送一個非同步的 call 請求,並將其加入請求識別碼集合。

Request 傳送至由 ServerRef 識別的 gen_statem 程序。Label 將與操作的請求識別碼關聯,並新增到傳回的請求識別碼集合 NewReqIdCollection。稍後可以透過將集合作為引數傳遞給 receive_response/3wait_response/3check_response/3,從此集合中取得對應於請求的回覆。

與呼叫 reqids_add(​send_request(ServerRef, Request),Label, ReqIdCollection) 相同,但效率略高。

此函數的連結

start(Module, Args, Opts)

檢視原始碼 (自 OTP 19.0 起)
-spec start(Module :: module(), Args :: term(), Opts :: [start_opt()]) -> start_ret().

啟動一個伺服器,既不連結也不註冊。

等同於 start/4,差別在於 gen_statem 程序未向任何 名稱服務註冊。

此函數的連結

start(ServerName, Module, Args, Opts)

檢視原始碼 (自 OTP 19.0 起)
-spec start(ServerName :: server_name(), Module :: module(), Args :: term(), Opts :: [start_opt()]) ->
               start_ret().

啟動一個已註冊但未連結的伺服器。

根據 OTP 設計原則 (使用 proc_lib 原始物件) 建立獨立的 gen_statem 程序。由於它未連結到呼叫程序,因此監管程式無法使用此啟動函數來啟動子程序。

如需引數和傳回值的描述,請參閱 start_link/4

此函數的連結

start_link(Module, Args, Opts)

檢視原始碼 (自 OTP 19.0 起)
-spec start_link(Module :: module(), Args :: term(), Opts :: [start_opt()]) -> start_ret().

啟動一個已連結但未註冊的伺服器。

等同於 start_link/4,差別在於 gen_statem 程序未向任何 名稱服務註冊。

此函數的連結

start_link(ServerName, Module, Args, Opts)

檢視原始碼 (自 OTP 19.0 起)
-spec start_link(ServerName :: server_name(), Module :: module(), Args :: term(), Opts :: [start_opt()]) ->
                    start_ret().

啟動一個已連結且已註冊的伺服器。

根據 OTP 設計原則 (使用 proc_lib 原始物件) 建立 gen_statem 程序,該程序會產生並連結到呼叫程序。當 gen_statem 必須是監管樹的一部分時,這一點至關重要,以便它連結到其監管程式。

產生的 gen_statem 程序會呼叫 Module:init/1 來初始化伺服器。為確保同步的啟動程序,start_link/3,4 會在 Module:init/1 傳回或失敗之前才傳回。

ServerName 指定要為 gen_statem 程序註冊的 server_name/0。如果 gen_statem 程序是以 start_link/3 啟動的,則不會提供 ServerName,並且不會註冊 gen_statem 程序。

Module 是回呼模組的名稱。

Args 是一個任意詞彙,會作為引數傳遞給 Module:init/1

Opts 中的啟動選項

傳回值

如果傳回值是 ignore{error, _},則已啟動的 gen_statem 程序會終止。如果將 'EXIT' 訊息傳送到呼叫程序(由於程序連結),則該訊息已被消耗。

警告

在 OTP 26.0 之前,如果已啟動的 gen_statem 程序從 Module:init/1 傳回例如 {stop, Reason},則此函數可能會在已啟動的 gen_statem 程序終止之前傳回 {error, Reason},因此再次啟動可能會失敗,因為虛擬機資源(例如已註冊的名稱)尚未取消註冊,並且 'EXIT' 訊息可能會稍後傳送到呼叫此函數的程序。

但是,如果已啟動的 gen_statem 程序在 Module:init/1 期間失敗,則程序連結 {'EXIT', Pid, Reason} 訊息會導致此函數傳回 {error, Reason},因此 'EXIT' 訊息已被消耗,且已啟動的 gen_statem 程序已終止。

由於無法從 start_link/3,4 的傳回值區分這兩種情況,因此此不一致已在 OTP 26.0 中清除。

此函數的連結

start_monitor(Module, Args, Opts)

檢視原始碼 (自 OTP 23.0 起)
-spec start_monitor(Module :: module(), Args :: term(), Opts :: [start_opt()]) -> start_mon_ret().

啟動一個已監控但既不連結也不註冊的伺服器。

start_monitor/4 等效,只是 gen_statem 程序未向任何 名稱服務註冊。

此函數的連結

start_monitor(ServerName, Module, Args, Opts)

檢視原始碼 (自 OTP 23.0 起)
-spec start_monitor(ServerName :: server_name(),
                    Module :: module(),
                    Args :: term(),
                    Opts :: [start_opt()]) ->
                       start_mon_ret().

啟動一個已監控且已註冊但未連結的伺服器。

根據 OTP 設計原則(使用 proc_lib 原語)建立獨立的 gen_statem 程序,並以原子方式設置對新建立程序的監視器。

由於已啟動的程序未連結到呼叫程序,因此監督者無法使用此啟動函數來啟動子程序。

有關引數和傳回值的說明,請參閱 start_link/4,但請注意,若啟動成功,則傳回值會不同,因為此函數會傳回 {ok, {Pid, Mon}},其中 Pid 是程序的程序識別碼,而 Mon 是程序的監視器參考。如果啟動不成功,則呼叫者將被封鎖,直到收到 DOWN 訊息並從呼叫者的訊息佇列中移除。

此函數的連結

stop(ServerRef)

檢視原始碼 (自 OTP 19.0 起)
-spec stop(ServerRef :: server_ref()) -> ok.

等同於 stop(ServerRef, normal, infinity)

此函數的連結

stop(ServerRef, Reason, Timeout)

檢視原始碼 (自 OTP 19.0 起)
-spec stop(ServerRef :: server_ref(), Reason :: term(), Timeout :: timeout()) -> ok.

停止一個伺服器。

命令 gen_statem ServerRef 以指定的 Reason 退出,並等待其終止。gen_statem 會在退出之前呼叫 Module:terminate/3

如果伺服器以預期的原因終止,則此函數會傳回 ok。任何其他原因(而非 normalshutdown{shutdown, Term})都會導致透過 logger 發出錯誤報告。具有相同原因的退出訊號會傳送到已連結的程序和埠。預設的 Reasonnormal

Timeout 是大於 0 的整數,指定等待伺服器終止的毫秒數,或是原子 infinity 來無限期等待。預設為 infinity。如果伺服器未在指定的時間內終止,則呼叫會以原因 timeout 退出呼叫程序。

如果程序不存在,則呼叫會以原因 noproc 退出呼叫程序,或者如果伺服器執行的遠端 Node 的連線失敗,則會以原因 {nodedown, Node} 退出。

此函數的連結

wait_response(ReqId)

檢視原始碼 (自 OTP 23.0 起)
-spec wait_response(ReqId) -> Result
                       when
                           ReqId :: request_id(),
                           Response ::
                               {reply, Reply :: term()} | {error, {Reason :: term(), server_ref()}},
                           Result :: Response | timeout.

等同於 wait_response(ReqId, infinity)

此函數的連結

wait_response(ReqId, WaitTime)

檢視原始碼 (自 OTP 23.0 起)
-spec wait_response(ReqId, WaitTime) -> Result
                       when
                           ReqId :: request_id(),
                           WaitTime :: response_timeout(),
                           Response ::
                               {reply, Reply :: term()} | {error, {Reason :: term(), server_ref()}},
                           Result :: Response | timeout.

等待請求的回應。

等待要求識別碼 ReqId 的回應。該要求必須是由 send_request/2gen_statem 程序發出。此函數必須從呼叫 send_request/2 的同一個程序呼叫。

WaitTime 指定等待回覆的時間長度。如果在指定的時間內未收到回覆,則函數會傳回 timeout,且不會執行清除動作。因此,可以重複呼叫該函數,直到傳回回覆為止。

請參閱 call/3,了解如何處理請求以及 gen_statem 伺服器如何傳送 Reply

如果在等待回覆時 gen_statem 伺服器處理程序已死或死亡,則會回傳一個包含結束 Reasonerror 回傳值。

receive_response/2wait_response/2 之間的差異在於,receive_response/2 會在逾時時放棄要求,以便忽略潛在的未來回應,而 wait_response/2 則不會。

此函數的連結

wait_response(ReqIdCollection, WaitTime, Delete)

檢視原始碼 (自 OTP 25.0 起)
-spec wait_response(ReqIdCollection, WaitTime, Delete) -> Result
                       when
                           ReqIdCollection :: request_id_collection(),
                           WaitTime :: response_timeout(),
                           Delete :: boolean(),
                           Response ::
                               {reply, Reply :: term()} | {error, {Reason :: term(), server_ref()}},
                           Result ::
                               {Response,
                                Label :: term(),
                                NewReqIdCollection :: request_id_collection()} |
                               no_request | timeout.

等待集合中任何請求的回應。

ReqIdCollection 中等待回應。ReqIdCollection 的所有要求識別碼必須對應於使用 send_request/2send_request/4 發出的要求,而且所有要求都必須由呼叫此函數的程序發出。

回應中的 Label 是與該回應對應的請求識別碼相關聯的 Label。請求識別碼的 Label 會在將請求 ID 新增至集合時,或使用 send_request/4 發送請求時關聯。

wait_response/2 相比,與特定要求識別碼關聯的傳回結果或例外狀況會包裝在 3 元組 {Response, Label, NewReqIdCollection} 中。Responsewait_response/2 會產生的值,Label 是與特定 要求識別碼關聯的值,而 NewReqIdCollection 是可能會修改的要求識別碼集合。

如果 ReqIdCollection 為空,則會回傳 no_request

如果在 WaitTime 過期之前未收到回應,則會傳回 timeout。在收到回應並由 check_response()receive_response()wait_response() 完成之前,可以根據需要多次繼續等待回應。

receive_response/3wait_response/3 之間的差異在於,receive_response/3 會在逾時時放棄請求,因此會忽略潛在的未來回應,而 wait_response/3 則不會。

如果 Deletetrue,則與 Label 的關聯已從產生的 NewReqIdCollection 中的 ReqIdCollection 中刪除。如果 Deletefalse,則 NewReqIdCollection 會等於 ReqIdCollection。請注意,刪除關聯不是免費的,而且包含已處理要求集合的仍然可以由後續對 wait_response/3check_response/3receive_response/3 的呼叫使用。

但是,如果不刪除已處理的關聯,則上述呼叫將無法偵測到何時沒有更多未完成的要求要處理,因此您必須以其他方式追蹤此情況,而不是依賴 no_request 傳回。請注意,如果您將僅包含已處理或放棄要求的關聯的集合傳遞給此函數,則它將始終封鎖,直到 WaitTime 過期,然後傳回 timeout