Process is a very basic unit of task execution for elixir. Previously, we did not understand spawn and spawn_link deeply. We only know that the latter is the link between the child process and the parent process, and the accidental collapse of the child process can also lead to the collapse of the parent process. Today I just saw a film about it. The exit_trap process flag of erlang The introduction gives us a deeper understanding of these two concepts.
For generating a new process to perform time-consuming operations:
1. I don't care if the child process hangs up. I choose spawn.
2. I want the child process to hang up and the parent process to stop, so I choose spawn_link.
3. I hope that when the child process hangs up, it will notify the parent process (sending messages, communication between processes using message mechanism, which can avoid deadlock problems caused by multi-threaded shared memory). Process.flag(:trap_exit, true), spawn_link
#some logic here
#need to do some other things that is time-consuming
spawn fn ->
#do some stuff here
nil
end
spawn_link fn ->
#do some stuff here
nil
end
Process.flag(:trap_exit, true)
spawn_link fn ->
#do some stuff here
nil
end
Write a column below to see how the three approaches differ.
defmodule Process.Test do
def test1 do
spawn fn ->
IO.puts "I'm a new process and i will crash"
Process.sleep 2000
exit(:abnormal)
end
end
def test2 do
spawn_link fn ->
IO.puts "I'm a new process and i will crash"
Process.sleep 2000
exit(:abnormal)
end
end
def test3 do
Process.flag(:trap_exit, true)
spawn_link fn ->
IO.puts "I'm a new process and i will crash"
Process.sleep 2000
exit(:abnormal)
end
end
end
The above code defines a module that contains three functions to represent three methods of generating new processes, respectively.
Open an IEX (my environment is linux)
Interactive Elixir (1.4.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> defmodule Process.Test do
...(1)> def test1 do
...(1)> spawn fn ->
...(1)> IO.puts "I'm a new process and i will crash"
...(1)> Process.sleep 2000
...(1)> exit(:abnormal)
...(1)> end
...(1)> end
...(1)>
...(1)> def test2 do
...(1)> spawn_link fn ->
...(1)> IO.puts "I'm a new process and i will crash"
...(1)> Process.sleep 2000
...(1)> exit(:abnormal)
...(1)> end
...(1)> end
...(1)>
...(1)> def test3 do
...(1)> Process.flag(:trap_exit, true)
...(1)> spawn_link fn ->
...(1)> IO.puts "I'm a new process and i will crash"
...(1)> Process.sleep 2000
...(1)> exit(:abnormal)
...(1)> end
...(1)> end
...(1)> end
{:module, Process.Test,
<<70, 79, 82, 49, 0, 0, 8, 124, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 186,
131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115,
95, 118, 49, 108, 0, 0, 0, 4, 104, 2, ...>>, {:test3, 0}}
iex(2)>
Let's execute the functions in the module one by one, starting with function test1
iex(3)> Process.Test.test1
I'm a new process and i will crash
#PID<0.113.0>
We can see that nothing happens except the printed information. In fact, the child process has been abnormally exited by the exit function, but we do not see any error message. The main reason is that spawn is used. The current process does not care about the running state of the process. Okay, let's move on to the next one.
iex(4)> Process.Test.test2
I'm a new process and i will crash
#PID<0.115.0>
** (EXIT from #PID<0.80.0>) :abnormal
Interactive Elixir (1.4.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>
This time we can see ** (EXIT from # PID < 0.80.0>): the information of abnormal, and we can also see that iex reprinted the startup information iex serial number from 4 to 1. Exceptional exit on behalf of the child process does end the normal execution of the parent process.
iex(1)> Process.Test.test3
I'm a new process and i will crash
#PID<0.119.0>
While continuing to execute the test3 function, the same result as test1 appears. This is the process flag: trap_exit is working. spawn_link does not cause the parent process to exit as well. But there seems to be another problem: what about the message that the child process abnormally exits?
In fact, the parent process has received the message, but there is no handler processing mechanism (no printing) we can slightly change the program, add receive function in test3.
defmodule Process.Test do
def test3 do
Process.flag(:trap_exit, true)
spawn_link fn ->
IO.puts "I'm a new process and i will crash"
Process.sleep 2000
exit(:abnormal)
end
receive do
msg ->
IO.inspect msg
_ ->
nil
end
end
end
Copy the code into the iex just now (although the module is the same name here, but do not close the previous iex but copy and paste directly, elixir hot update support repl directly replace the code module)
{:module, Process.Test,
<<70, 79, 82, 49, 0, 0, 8, 228, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 186,
131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115,
95, 118, 49, 108, 0, 0, 0, 4, 104, 2, ...>>, {:test3, 0}}
iex(2)>
Run test3 function again
iex(3)> Process.Test.test3
I'm a new process and i will crash
{:EXIT, #PID<0.120.0>, :abnormal}
{:EXIT, #PID<0.120.0>, :abnormal}
At this point, we can clearly see that the abnormal exit of the child process (: abnormal)
Here's a brief introduction to the application scenario
Assume that the server needs to receive requests from the client in parallel and provide services for each request. At this point, if we need to care about whether the service can be successfully completed, we need to use spawn_link or Process.monitor to monitor the application service process. For the abnormal exit of the sub-application service, when we receive the message, we can choose the appropriate measures (restart). Here, the receive used in the routine is blocking, just for demonstration. GenServer's handle_info can be used to receive and process the message in the actual project. As for the message structure of process exit, if Process.monitor pid is used, every PID process {DOWN, ref,: process,_pid, reason} will be sent to the parent process calling monitor to listen on, handle_info can be used to process the message.
defmodule Test do
use GenServer
def monitor_me pid do
Process.monitor pid
#Here, the pid can be stored in the ets table, and when handle_info receives the message, it can match the pid to do the corresponding processing.
end
def handle_info({:DOWN, ref, :process, _pid, reason}, state) do
#do something here when receive this message
{:noreply, state}
end
end