进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
操纵进程:
1.派生和链接
进程派生函数有两个:第一个函数仅有一个参数,就是用作新进程入口的(空元)fun函数;另一个则需要模块名、函数名和参数列表三个参数。
所有这些派生的函数都会返回新进程的进程标识符,通过标识符父进程可以和新进程通信。然而新进程对父进程却一无所知,只能通过其他方式获悉相关消息。
2.进程监视
链接有一个替代品,称作监视。这是一种单向链接,可以让一个进程在不影响目标的情况下对目标进程施行监视。
Ref = monitor(process, Pid)
由Pid标识的进程一旦退出,实施监视的进程将会受到一条含有唯一引用的Ref消息。
3.靠抛异常来终结进程
exit类异常用于终止运行中的进程。除非被进程捕获,否则该调用将令进程终止。
4.直接向进程发送退出信号
进程除了在意外退出时会自动发送信号外,还可以直接向其他进程发出退出信号。收发的双方无需链接: exit(Pid, Reason) 注意:该信号终止的不是发送方,而是接收方。
5.设置trap_exit标志
一旦接收到来自相互链接的其他进程的退出信号,默认情况下进程就会退出。为了避免这种行为并捕捉到退出信号,可以给进程设置trap_exit标志: process_flag(trap_exit, true) 如此一来,除了(kill)信号无法捕捉外,其他外来的退出信号都会被转换成无害的消息。
接收消息与选择性接收:
接收消息的进程可以通过receive表达式从信箱队列中提取消息。尽管接收到的消息都是按照先后顺序排列,接收方仍然可以决定提取哪条消息。选择性的忽略当前不太重要的信息,这是erlang进程通信的一个关键特性。
每次执行时,receive会先检查最老的消息(位于队列头部),像在case表达式中那样尝试将消息与子句匹配,如果找不到匹配的子句,就几句检查下一条。如果某条子句的模式与消息匹配成功,且保护式成立,该消息就会被移除信箱,同时与子句所对应的正文将被执行。如果一直等到超时也没有等到匹配的消息,那么超时部分对应的正文将会被执行,信箱维持原样。
注册进程:
Erlang系统都会有一个本地进程注册表——这是一个用于注册进程的简单命名服务。一个名称一次只能用于一个进程,换言之这种情况仅适用于单例模式:一般都是些系统服务,这些服务在每个运行时系统中同一时刻最多只能有一个实例。
启动erlang shell并调用内置函数registered(),如下图所示:
Erlang系统本身很像是跑着一组重要系统服务的操作系统。使用内置函数whereis()可以查找当前与指定注册名对应的pid,如下图所示:
下面这种情况完全体现出了注册进程的主要优点:假设某注册进程崩溃,对应的服务被重启,新进程的进程标识符会发生变化。此时无需将新进程服务的新pid逐个通知给系统中的所有进程,只要更新进程注册表即可。(但在服务完成重启和注册之前,会存在一段时间的真空期,期间该名称不指向任何进程。)
消息投递与信号:
Erlang进程之间用!运算符互相发送消息,这种消息只是erlang通用信号系统的一种特殊形式。有一大类信号是濒死进程向与之链接的相邻进程发送的退出信号;还有一小类信号是程序员不可见的,比如尝试链接两个进程时发送的链接请求。
投递信号有两种基本投递保障对所有信号都成立:①如果进程P1向同一个目标进程P2先后发送了S1和S2两个信号(不论进程在发送S1与S2之间做了什么,也不论两个信号之间发送间隔多久),这两个信号按发送顺序抵达P2(如果都能到)。如此一来,就说明退出信号绝不会掩盖进程临死前的最后一条消息,消息也绝不会掩盖链接请求。这是erlang进程间通信的基础。②尽力投递所有信号。同一erlang运行时系统内,进程之间不会存在消息丢失的危险。但是在两个需要网络互联的erlang系统之间,一旦网络断开链接,就存在丢失消息的可能(部分依赖传输协议)。连接回复之后,有可能出现上例中S2抵达S1丢失的情况。