Go语言是一门工程语言。
多数系统级语言(Java和C#)的根本编程哲学来源于C++,将C++的面向对象的进一步发扬光大。
而Go语言则认为C才是学习的对象,它足够简单:互联网时代的C语言。简单到没有多余的成分。
那么,互联网时代的C语言需要考虑哪些关键问题呢?
1)首先,并行与分布式支持。多核化和集群化是互联网时代的典型特征。作为一个互联网时代的C语言,必须要让这门语言操作多核计算机与计算机集群如同操作单机一样容易。
2)其次,软件工程支持。工程规模不断扩大是产业发展的必然趋势。单机时代语言可以只关心问题本身的解决,而互联网时代的C语言还需要考虑软件品质保障和团队协作相关的话题。
3)最后,编程哲学的重塑。计算机软件经历了数十年的发展,形成了面向对象等多种学术流派。
什么才是最佳的编程实践?作为互联网时代的C语言,需要回答这个问题。
大写的东西(方法/变量/常量)包外可见。
字面常量是无类型的。const来定义常量,这样可以给字面常量一个名字。常量赋值是一个编译器行为,任何在运行期才能算出来的量赋值给const都是不行的。
字符串是基本类型,也是不可变对象,初始化之后不能像string[0] = 'x'被赋值
数组在方法参数中是引用值传递(副本传递)
Go语言中函数名字的大小写不仅仅是风格,更直接体现了该函数的可见性,这一点尤其需要注意。
与C、C++和Java等开发语言的一个极大不同在于,Go语言的函数或者成员的方法可以有多个返回值。
可以给函数的返回值命名,就像函数的输入参数一样。
返回值被命名之后,它们的值在函数开始的时候被自动初始化为空。在函数中执行不带任何参数的return语句时,会返回对应的返回值变量的值。
在Go里面,函数可以像普通变量一样被传递或使用;Go语言支持随时在代码里定义匿名函数。
漂亮的错误处理规范是Go语言最大的亮点之一。
优雅之处在于,Go语言对面向对象编程的支持是语言类型系统中的天然组成部分。
类型系统:值语义;引用语义
与Java不同的是,go的值类型,甚至是预置的类型都能被赋予行为/方法
Go语言也提供了继承,但是采用了组合的文法,所以我们将其称为匿名组合
Go语言官方网站提供的Effective Go
Go语言中符号的可访问性是包一级的而不是类型一级的。这意味着小写的东西在同一个包中都是可以被访问的。
如果说goroutine和channel 是支撑起Go语言的并发模型的基石,让Go语言在如今集群化与多核化的时代成为一道极为亮丽的风景,那么接口是Go语言整个类型系统的基石,让Go语言在基础编程哲学的探索上达到前所未有的高度。
Go语言在编程哲学上是变革派,而不是改良派。
在Go语言出现之前,接口主要作为不同组件之间的契约存在。对契约的实现是强制的(侵入式接口),你必须声明你的确实现了该接口。为了实现一个接口,你需要从该接口继承。
Go语言的非侵入式接口,看似只是做了很小的文法调整,实则影响深远。
有个对象a,则a是对象本身,var pointer *a = &a取a的地址(指针)赋值给*a类型(指针类型)的变量pointer,
func(pointer *a){*pointer}定义的是接收&a的参数,而方法里面的*pointer则取到a本身
一般来说,我们对于结构体(struct)的方法和属性的操作都是基于指针类型的。
并发程序的优势:
1)并发能更客观地表现问题模型;
2)并发可以充分利用CPU核心的优势,提高程序的执行效率;
3)并发能充分利用CPU与其他硬件设备固有的异步性。
并发的几种主流的实现模型:
1)多进程。多进程是在操作系统层面进行并发的基本模式。同时也是开销最大的模式。
2)多线程。多线程在大部分操作系统上都属于系统层面的并发模式,也是我们使用最多的最有效的一种模式。
3)基于回调的非阻塞/异步IO。这种架构的诞生实际上来源于多线程模式的危机。这种模式,编程比多线程要复杂,因为它把流程做了分割,对于问题本身的反应不够自然。
4)协程。协程(Coroutine)本质上是一种用户态线程,不需要操作系统来进行抢占式调度,且在真正的实现中寄存于线程中,因此,系统开销极小,可以有效提高线程的任务并发性,而避免多线程的缺点。使用协程的优点是编程简单,结构清晰;缺点是需要语言的支持,如果不支持,则需要用户在程序中自行实现调度器。目前,原生支持协程的语言还很少。
共享内存系统:共享内存系统是一种有效的并发模式,但是为了保证共享内存的有效性,需要做很多额外的处理,比如用锁的方式来避免资源的争用。
消息传递系统:对线程间共享状态的各种操作都被封装在线程之间传递的消息中,发送消息时对状态进行复制,并且在消息传递的边界上交出这个状态的所有权。代表Erlang
执行体是个抽象的概念,在操作系统层面有多个概念与之对应,比如操作系统自己掌管的进程(process)、进程内的线程(thread)以及进程内的协程(coroutine,也叫轻量级线程)。
Go 语言在语言级别支持轻量级线程,叫goroutine。
并发编程的难度在于协调,而协调就要通过交流、通信。在工程上,有两种最常见的并发通信模型:共享数据和消息。
消息机制认为每个并发单元是自包含的、独立的个体,并且都有自己的变量,但在不同并发单元间这些变量不共享。每个并发单元的输入和输出只有一种,那就是消息。这有点类似于进程的概念,每个进程不会被其他进程打扰,它只做好自己的工作就可以了。不同进程间靠消息来通信,它们不会共享内存。
channel是Go语言在语言级别提供的goroutine间的通信方式。channel是进程内的通信方式。跨进程一般使用socket或http协议进行通信。
声明:
一个能传递int类型数据的channel:var ch chan int
一个value是“能传递bool类型数据的channel” ,key是string类型的map: var m map[string] chan bool
初始化:
chint := make(chan int)
mapchbool := make(map[string] chan bool)
写入读取:
写入:ch <- value,value写入到channel
读取:value := <-ch,从channel读取value
将一个channel变量传递到一个函数时,可以通过将其指定为单向channel变量,从而限制该函数中可以对此channel的操作,比如只能往这个channel写,或者只能从这个channel读。
从设计的角度考虑,所有的代码应该都遵循“最小权限原则”
Go语言为并发性编程做了尽量多考虑的一种体现:once.Do(setup),once的Do()方法可以保证在全局范围内只调用指定的函数一次(这里指setup()函数),而且所有其他goroutine在调用到此语句时,将会先被阻塞,直至全局唯一的once.Do()调用结束后才继续。
网友评论