谈谈 process
erlang/elixir 是那种你用得越多,就越喜欢的语嫣。
一个多月前,我在 erlang 哪篇文章里,写下了这篇文字:
在 erlang 的世界里,万事万物都是 process。process 就像原子一样,组成分子,细胞,组织,器官,最终构成一副机体(见我的文章:boundary)。那么,process 究竟是什么?
其实它和 OS 的 process 概念几乎一样,就是 PCB(Process Control Block),以及围绕 PCB 的 heap,stack,mailbox 等内存空间。只不过,erlang 的 process 的 memory footprint 很小很小,一个刚初始化的 process,也就占用几百个 words(32bit CPU 下,1 word = 4 bytes,64bit CPU,1 word = 8 bytes)。所以,erlang VM 能创建的 process 理论上取决于内存的大小,我们可以轻易创建百万级,甚至千万级的 process(32bit CPU 的 pid 用 28 bit,64 bit CPU 的 pid 用 60 bit,大家可以自行算算 process 的上限)。erlang 的 process 源自 actor model 的思想,它是几种常见的 concurrency model 的一种(其它的 model 请参考我的文章:)。process 和 process 通过发送和接收消息来处理各种事务。
process 其实很像是 OOP 中的 object:process 之间是隔离的 —— 一个 process 无法访问另一个 process 的内部状态,这是封装的概念,而通过使用 parent/child process 也可完美实现继承和多态。实际上,erlang 算得上是 OOP 始祖 smalltalk 的正统的传承者(另一个渐渐淡出大家视野的传人是 ObjC)。
今天我们着重讲讲 process,以及我对 process 的一些不成熟的认知和使用方法。
表面上看 process 似乎和任何一门 OOP 语言中的 object 类似。它有 state,有 behavior,能接受外界的输入,并为这个输入返回结果,或者改变自己的状态。下面是一个非常简单的,仅用于 demo 的 kv store 的 python 版本:
class KVStore(object):
def __init__(self):
self.store = {}
def put(self, k, v):
self.store[k] = v
@memoize
def get(self, k):
return self.store[k] if k in self.store else None
elixir/OTP 版本:
defmodule KVStore do
use GenServer
# public interface
def start_link, do: GenServer.start_link(__MODULE__, nil, name: __MODULE__)
def put(k, v), do: GenServer.cast(__MODULE__, {:put, k, v})
def get(k), do: GenServer.call(__MODULE__, {:get, k})
# callback
def init(_), do: {:ok, %{}}
def handle_call({:get, k}, _from, state), do: {:reply, get(state, k), state}
def handle_cast({:put, k, v}, state), do: {:noreply, put(state, k, v)}
# private functions
defm get(state, k), do: Map.get(state, k)
def put(state, k, v), do: Map.put(state, k, v)
end
注:这里我们先别管 @memoize 和 defm 是干嘛的,稍后揭晓。
二者的调用代码,异曲同工,前者生成一个对象,后者生成一个 named process:
In [23]: store = KVStore()
In [24]: store.put("tyr", "chen")
In [25]: store.get("tyr")
Out[25]: 'chen'
iex(7)> KVStore.start_link()
{:ok, #PID<0.176.0>}
iex(8)> KVStore.put("tyr", "chen")
:ok
iex(9)> KVStore.get("tyr")
"chen"
实现同样的功能,就这个 case 而言,elixir 的代码量大大高于 python。然而,他们运行的状态截然不同。elixir 版本的 KV Store(下文称 EKV)生活在自己的世界里,有独立的人格;而 python 版本的 KV store(下文称 PKV)则严重依赖宿主 —— 运行的上下文是啥它就依附于啥,管他是 WSGI process 还是一个 CLI process。
(出自: