动态进程管理
掌握在监管者中动态启动和停止子进程,从而构建适应性强且高效利用资源的系统。
动态进程管理 是 CoddyKit 上的免费 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
Dynamic Processes Overview
In Erlang, not all processes need to be started when your application first boots. Sometimes, you need processes that are created and destroyed on demand.
These are called dynamic processes, and they are crucial for building adaptable and resource-efficient systems. Think of them as temporary workers that supervisors can hire and fire as needed.
Why Dynamic Management?
Dynamic process management offers several key advantages:
- Resource Efficiency: Only start processes when they are actually needed, saving memory and CPU.
- Adaptability: Respond to varying loads by scaling up or down the number of workers.
- On-Demand Tasks: Ideal for handling transient tasks like a new client connection, a file conversion request, or a single database query.
This contrasts with static children, which are always part of the supervisor's initial setup.
The `simple_one_for_one` Strategy
To manage dynamic children, supervisors often employ the simple_one_for_one restart strategy. This strategy is specifically designed for supervisors that will dynamically add children after startup.
- It allows you to add children without pre-defining them in the supervisor's
init/1function. - Each dynamically added child is treated as a unique entity, even if they share the same underlying module.
- If a dynamic child crashes,
simple_one_for_onewill restart only that child, not all of them.
Starting Dynamic Children
You start a dynamic child process using the supervisor:start_child/2 function. It takes two arguments:
SupRef: The name or PID of the supervisor.ChildSpec: A map (or a list of tuples in older Erlang) describing the child process.
This function returns {ok, ChildPid} or an error if the child couldn't be started.
Dynamic Child Specification
A ChildSpec for a dynamic child is similar to a static one, but the id field is crucial for distinguishing instances. Here's a typical structure:
id: A unique atom or term to identify this specific child instance (e.g.,client_123).start: A tuple{Module, Function, Args}to call for starting the child (e.g.,{gen_server, start_link, [{local, TaskId}, ?MODULE, [], []]}).type: Eitherworkerorsupervisor.restart: Oftentransientfor dynamic workers, meaning they only restart if they crash unexpectedly, not if they exit normally.shutdown: Timeout for graceful shutdown.
Code: Create Dynamic Workers
This example shows a supervisor starting two unique 'task' processes dynamically. Each task maintains its own count.
Run it to see how new processes are spawned and how you can interact with them individually.
-module(dynamic_start_example).
-behaviour(supervisor).
-export([start_link/0, init/1, start_task/1, call_task/2, run/0]).
-export([task_init/1, task_handle_call/3, task_terminate/2]).
% --- Supervisor part ---
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
Strategy = {simple_one_for_one, 0, 1},
Children = [],
{ok, {Strategy, Children}}.
start_task(TaskId) ->
io:format("Supervisor: Starting task ~p~n", [TaskId]),
ChildSpec = #{
id => TaskId,
start => {gen_server, start_link, [{local, TaskId}, ?MODULE, [], []]},
type => worker,
restart => transient,
shutdown => 5000
},
supervisor:start_child(?MODULE, ChildSpec).
call_task(TaskId, Message) ->
gen_server:call(TaskId, Message).
% --- Worker part (this module acts as a gen_server for the tasks) ---
task_init([]) ->
io:format("Task ~p: Initializing with count 0~n", [self()]),
{ok, 0}. % Initial state for the task
task_handle_call(get_count, _From, State) ->
io:format("Task ~p: Getting count ~p~n", [self(), State]),
{reply, State, State}.
task_handle_call({increment, Value}, _From, State) ->
NewState = State + Value,
io:format("Task ~p: Incrementing by ~p to ~p~n", [self(), Value, NewState]),
{reply, NewState, NewState}.
task_terminate(_Reason, State) ->
io:format("Task ~p: Terminating with final state ~p~n", [self(), State]),
ok.
% --- Entry point for runnable example ---
run() ->
io:format("~n--- Starting Dynamic Task Example ---~n"),
{ok, SupPid} = dynamic_start_example:start_link(),
io:format("Supervisor started: ~p~n", [SupPid]),
io:format("~nStarting Task 'task_alpha':~n"),
dynamic_start_example:start_task(task_alpha),
timer:sleep(100), % Give it a moment to start
Count1 = dynamic_start_example:call_task(task_alpha, get_count),
io:format("Task 'task_alpha' count: ~p~n", [Count1]),
dynamic_start_example:call_task(task_alpha, {increment, 7}),
Count2 = dynamic_start_example:call_task(task_alpha, get_count),
io:format("Task 'task_alpha' count after increment: ~p~n", [Count2]),
io:format("~nStarting Task 'task_beta':~n"),
dynamic_start_example:start_task(task_beta),
timer:sleep(100),
Count3 = dynamic_start_example:call_task(task_beta, get_count),
io:format("Task 'task_beta' count: ~p~n", [Count3]),
dynamic_start_example:call_task(task_beta, {increment, 12}),
Count4 = dynamic_start_example:call_task(task_beta, get_count),
io:format("Task 'task_beta' count after increment: ~p~n", [Count4]),
% In a real app, you'd stop the supervisor or individual tasks here.
% For this example, we'll let them run.
io:format("--- Dynamic Task Start Demo Finished ---~n"),
ok.Terminating Dynamic Children
Just as you can start processes dynamically, you can also stop them. The supervisor:terminate_child/2 function is used for this.
- It takes the
SupRefand theChildId(theidfrom the child specification) as arguments. - The supervisor will send an exit signal to the child, initiating a graceful shutdown.
- Once terminated, the child is removed from the supervisor's list, freeing up resources.
This is crucial for managing resources and ensuring processes don't linger unnecessarily.
Code: Terminate Dynamic Workers
This example demonstrates starting a task, interacting with it, and then gracefully stopping it using terminate_child/2. Notice the output when the task terminates.
-module(dynamic_stop_example).
-behaviour(supervisor).
-export([start_link/0, init/1, start_task/1, call_task/2, stop_task/1, run/0]).
-export([task_init/1, task_handle_call/3, task_terminate/2]).
% --- Supervisor part ---
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
Strategy = {simple_one_for_one, 0, 1},
Children = [],
{ok, {Strategy, Children}}.
start_task(TaskId) ->
io:format("Supervisor: Starting task ~p~n", [TaskId]),
ChildSpec = #{
id => TaskId,
start => {gen_server, start_link, [{local, TaskId}, ?MODULE, [], []]},
type => worker,
restart => transient,
shutdown => 5000
},
supervisor:start_child(?MODULE, ChildSpec).
call_task(TaskId, Message) ->
gen_server:call(TaskId, Message).
stop_task(TaskId) ->
io:format("Supervisor: Stopping task ~p~n", [TaskId]),
supervisor:terminate_child(?MODULE, TaskId).
% --- Worker part (this module acts as a gen_server for the tasks) ---
task_init([]) ->
io:format("Task ~p: Initializing with count 0~n", [self()]),
{ok, 0}.
task_handle_call(get_count, _From, State) ->
io:format("Task ~p: Getting count ~p~n", [self(), State]),
{reply, State, State}.
task_handle_call({increment, Value}, _From, State) ->
NewState = State + Value,
io:format("Task ~p: Incrementing by ~p to ~p~n", [self(), Value, NewState]),
{reply, NewState, NewState}.
task_terminate(_Reason, State) ->
io:format("Task ~p: Terminating with final state ~p~n", [self(), State]),
ok.
% --- Entry point for runnable example ---
run() ->
io:format("~n--- Terminating Dynamic Task Example ---~n"),
{ok, SupPid} = dynamic_stop_example:start_link(),
io:format("Supervisor started: ~p~n", [SupPid]),
io:format("~nStarting Task 'temp_task':~n"),
dynamic_stop_example:start_task(temp_task),
timer:sleep(100),
dynamic_stop_example:call_task(temp_task, {increment, 100}),
Count = dynamic_stop_example:call_task(temp_task, get_count),
io:format("Task 'temp_task' current count: ~p~n", [Count]),
io:format("~nStopping Task 'temp_task':~n"),
dynamic_stop_example:stop_task(temp_task),
timer:sleep(100), % Give it a moment to terminate
io:format("~nAttempting to call 'temp_task' after termination (will fail):~n"),
CatchResult = try dynamic_stop_example:call_task(temp_task, get_count) of
Result -> Result
catch
error:Reason -> {error, Reason}
end,
io:format("Call result after stop: ~p~n", [CatchResult]),
supervisor:stop(SupPid), % Clean up the supervisor
io:format("Supervisor stopped.~n"),
io:format("--- Dynamic Task Stop Demo Finished ---~n"),
ok.Practical Use Cases
Dynamic process management is a powerful tool in Erlang. Here are some common scenarios where it shines:
- Web Servers: Spawning a new process for each incoming client connection.
- Connection Pools: Managing a pool of database connections, creating new ones as needed.
- Batch Processors: Starting worker processes to handle items from a queue.
- User Sessions: Managing individual user sessions in a multi-user application.
It allows your system to adapt to varying loads and efficiently manage transient resources.
Quick Check
You've learned about starting and stopping dynamic child processes. Let's test your understanding.
Recap: Dynamic Processes
We've explored dynamic process management, a key technique for building adaptable Erlang systems:
- Dynamic processes are created and destroyed on demand, saving resources.
- The
simple_one_for_onestrategy is typically used by supervisors managing dynamic children. supervisor:start_child/2is used to add new processes, providing a unique child specification for each.supervisor:terminate_child/2ensures graceful shutdown and removal of dynamic processes.
Mastering this allows your applications to efficiently scale and respond to changing demands.
用 AI 导师学习 Erlang — 免费
在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。
- 课程
- 12
- 课程
- 48
常见问题解答
「动态进程管理」课时是免费的吗?
是的 — 「动态进程管理」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 课程的其余内容,请升级到 CoddyKit PRO。 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 课程共包含 4 节课。
「动态进程管理」这节课中我会学到什么?
掌握在监管者中动态启动和停止子进程,从而构建适应性强且高效利用资源的系统。 你通过在浏览器中直接运行的动手代码来练习 Erlang OTP: Distributed & Fault-Tolerant Systems Programming,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「动态进程管理」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 课中编写并运行代码吗?
能。每节 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。