// The error built-in interface type is the conventional interface for // representing an error condition, with the nil value representing no error. type error interface { Error() string }
const a = iota // iota = 0 const b = iota // iota = 0
所以利用iota和自动补全,可以方便的进行多个常量的初始化。
比如go内置的log包,预定义log打印格式时,就使用iota进行。
1 2 3 4 5 6 7 8 9 10
const ( Ldate = 1 << iota // the date in the local time zone: 2009/01/23 Ltime // the time in the local time zone: 01:23:23 Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime. Llongfile // full file name and line number: /a/b/c/d.go:23 Lshortfile // final file name element and line number: d.go:23. LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone Lmsgprefix // move the "prefix" from the beginning of the line to before LstdFlags = Ldate | Ltime // initial values for the standard logger )
// byte is an alias for uint8 and is equivalent to uint8 in all ways. It is // used, by convention, to distinguish byte values from 8-bit unsigned // integer values. type byte = uint8
// rune is an alias for int32 and is equivalent to int32 in all ways. It is // used, by convention, to distinguish character values from integer values. type rune = int32
byte和unit8,rune和int32是完全一样的。
类型别名和类型定义语法上的区别就是多了个=
1 2 3 4 5
var a rune var b int32 = 132 a = b //可以直接赋值 var c int = 132 // t.Log(a == c) 类型不匹配,不可以直接比较
const a myInt = 100 const b int = a + 10 // 编译报错:cannot use a + 10 (constant 110 of type myInt) as int value in constant const b int = int(a) + 10 // ok
但是如果定义常量的时候不指定类型,就是无类型常量
1 2 3 4 5
type myInt int const a myInt = 100 // 无类型常量,可以编译 const n = 7 const b myInt = a + n
这时候用到无类型常量的另一个特性:隐式类型转换
隐式类型转换
无类型常量也不是说就真的没有类型,它也有自己的默认类型,不过它的默认类型 是根据它的初值形式来决定的。像上面代码中的常量 n 的初值为整数形式,所以它的默认 类型为 int。
Go 语言里不存在像 Java 等编程语言中那种令人困惑的“传值或传引用”问题。在 Go 语言中,我们判断所谓的“传值”或者“传引用”只要看被传递的值的类型就好了。如果传递的值是引用类型的,那么就是“传引用”。如果传递的值是值类型的,那么就是“传 值”。从传递成本的角度讲,引用类型的值往往要比值类型的值低很多。
index := rand.Intn(3) intChannels[index] <- index t.Logf("向channe%d发送", index)
select { case e := <-intChannels[0]: { t.Logf("0接收到:%d", e) } case e := <-intChannels[1]: t.Logf("1接收到:%d", e) case e := <-intChannels[2]: t.Logf("2接收到:%d", e) default: t.Log("都没选中") }
Go 语言不但有着独特的并发编程模型,以及用户级线程 goroutine,还拥有强大 的用于调度 goroutine、对接系统级线程的调度器。
这个调度器是 Go 语言运行时系统的重要组成部分,它主要负责统筹调配 Go 并发编程模型中的 三个主要元素,即:G(goroutine 的缩写)、P(processor 的缩写)和 M(machine 的缩 写)。 其中的 M 指代的就是系统级线程。而 P 指的是一种可以承载若干个 G,且能够使这些 G 适时 地与 M 进行对接,并得到真正运行的中介
从宏观上说,G 和 M 由于 P 的存在可以呈现出多对多的关系。当一个正在与某个 M 对接并运 行着的 G,需要因某个事件(比如等待 I/O 或锁的解除)而暂停运行的时候,调度器总会及时 地发现,并把这个 G 与那个 M 分离开,以释放计算资源供那些等待运行的 G 使用。 而当一个 G 需要恢复运行的时候,调度器又会尽快地为它寻找空闲的计算资源(包括 M)并安 排运行。另外,当 M 不够用时,调度器会帮我们向操作系统申请新的系统级线程,而当某个 M 已无用时,调度器又会负责把它及时地销毁掉。
正因为调度器帮助我们做了很多事,所以我们的 Go 程序才总是能高效地利用操作系统和计算机 资源。
主 goroutine
与一个进程总会有一个主线程类似,每一个独立的 Go 程序在运行时也总会有一个主 goroutine。这个主 goroutine 会在 Go 程序的运行准备工作完成后被自动地启用,并不需要我 们做任何手动的操作。
主 goroutine 的go函数就是那个作为程序入口的main函数
当程序执行 到一条go语句的时候,Go 语言的运行时系统,会先试图从某个存放空闲的 G 的队列中获取一 个 G(也就是 goroutine),它只有在找不到空闲 G 的情况下才会去创建一个新的 G。
然而,创建 G 的成本也是非常低的。创建一个 G 并不会像新建一个进程或者一个系统级线程那 样,必须通过操作系统的系统调用来完成,在 Go 语言的运行时系统内部就可以完全做到了
在拿到了一个空闲的 G 之后,Go 语言运行时系统会用这个 G 去包装当前的那个go函数(或者 说该函数中的那些代码),然后再把这个 G 追加到某个存放可运行的 G 的队列中。