Erlang 入门-模块和进程

什么是模块

在前面的教程中,编写的代码都是在Erlang的shell中进行的。而交互式shell也被认为是绝大部分动态语言的特性之一。但是,任何一个项目不可能都是靠在shell中输入代码完成的,那么代码必须保存在某处。保存的代码就是模块,模块就是一个包含了大量函数的文件,因为模块是文件,就一定要有一个名字。

如何编写一个模块

编写模块非常简单,只要打开任意一款文本编辑器就可以了(推荐使用VScode,当然如果你喜欢命令行我十分乐意的推荐你使用Emacs),然后将我们前面教程中的函数放入其中,但是这并不能形成一个模块。

Erlang定义一个模块必须满足下面两点 1. 声明属性,这些属性描述模块自身的特质,例如模块的名字 1. 声明函数。

声明函数已经完成了,那么如何声明属性呢?Erlang的模块一般会定义模块名和导出函数,定义方法如下 Erlang -module(Name). %% 声明模块名,模块名必须是原子 -export([Function1/Arity, Function2/Arity, ..., FunctionN/Arity]). %% 声明函数导出 下面就用就用Erlang 入门-命名函数中的greet函数编写一个msg模块: ``` Erlang -module(msg). -export([greet/2]).

greet(male, Name) → io:format(“Hello, Mr. ~s!~n”, [Name]); greet(female, Name) → io:format(“Hello, Mrs. ~s!~n”, [Name]); greet(_, Name) → io:format(“Hello, ~s!~n”, [Name]). ```

如何在shell使用模块

先假定msg.erl文件保存在/home/david/erl目录下,我们在这个目录下用erl命令打开Erlang的交互式shell ``` Erlang Erlang/OTP 20 [erts-9.1.5] [64-bit] [ds:8:8:10] [hipe] [dtrace]

Eshell V9.1.5 (abort with ^G) 1> c(“msg”). {ok,msg} 2>msg:greet(male,“David”). Hello, Mr. David! ok 3> `` 可以看到,当函数放到模块中的时候,调用函数就需要在函数前面增加模块名字和冒号,如msg:greet(male,“David”).`

什么是Erlang的进程

在Erlang中,最小执行单位是进程,当然这个进程并不是系统层面上的进程,也不是系统的线程线程,而是Erlang运行时中自己定义的一种轻量级的执行结构。

Erlang的进程是如何执行的

在上面提到Erlang的进程是Erlang运行时中定义的一种轻量级的执行结构,那么这个结构就是由Erlang的运行时来进行调度。Erlang运行时在启动之后会创建出和内核数量相同的系统线程,每个线程上都绑定了一个Erlang虚拟机的CPU,然后Erlang通过调度算法将就绪的Erlang进程调度到这个虚拟机的CPU上。

下面是调度逻辑的伪代码: C pid schedule() { static int majorReductions = MREDS; // 计算自己执行多少个时钟 majorReductions--; // 减少时钟 if(majorReductions == 0) { externalPoll(); // 时钟为0,检查外部事件,主要是socket majorReductions = MREDS; // 重置时钟 } checkTimeouts(); // 检查超时 pid p = nextReady(readyQueue); // 在准备就绪队列中找出就绪的进程 p->reductions = REDS; // 给进程分配时间片 p->status = RUNNING; // 标记运行 return p; // 返回给CPU } 是不是非常像一个操作系统在调度进程?因为Erlang的运行时,本身就在模拟一个特殊的基于寄存器的CPU,以及上面的一个任务调度器(最简单OS)。

如何创建一个Erlang进程

Erlang做为一个面向并发的计算机语言,它为我们提供了两个比较有意思的进程创建元语erlang:spawnerlang:spawn_link。这两个元语唯一的差别是erlang:spawn在创建一个Erlang进程成功后就不管这个进程了。而erlang:spawn_link会将创建者和被创建者关联起来,如果创建者异常退出了,被创建者也会跟着退出,反之亦然。

下面就用刚才建立好的msg模块演示下如何创建Erlang进程 ``` Erlang Erlang/OTP 20 [erts-9.1.5] [64-bit] [ds:8:8:10] [hipe] [dtrace]

Eshell V9.1.5 (abort with ^G) 1> c(“msg”).
{ok,msg} 2> erlang:spawn(msg,greet,[male,“David”]). Hello, Mr. David! <0.67.0> 3> erlang:spawn_link(msg,greet,[male,“David”]). Hello, Mr. David! <0.69.0> 4> ``` 在这里spawn_link创建的进程因为是正常退出,并没有引起shell的崩溃,在后面介绍异常的时候,将会讲解Erlang进程的异常退出和如何应对。

Erlang进程的特点

Erlang为什么要这么大费周章的去设置这样一种进程机制呢?因为它有以下特点:

  1. 可抢占的软实时,这是Go,lua以及Akka库做不到的,毕竟Erlang最初设计是给电话交换机使用
  2. 轻量级,可以快速创建和大量创建,在CPU和内存充足的条件下,一个Erlang运行时环境可以创建上百万的Erlang进程
  3. 可监控,Erlang进程是可以通过监控机制进行管理,在异常退出的场景下,快速恢复。

总结

虽然已经讲述了如何进行进程创建了,但是大家很快就发现了吧,进程在执行完函数就立刻退出了,同时通过前面的文章知道Erlang中的变量是不可变的,那么Erlang进程要如何交换数据呢。在后面的文章,将会逐步介绍消息传递,异常处理等知识。

Power by Vultr and AiWiki
Copyright © 2019 David Fox All Rights Reserved.