GOMAXPROCS
确定;GOMAXPROCS
限定P的个数协程:coroutine。也叫轻量级线程。
与传统的系统级线程和进程相比,协程最大的优势在于“轻量级”。可以轻松创建上万个而不会导致系统资源衰竭。而线程和进程通常很难超过1万个。这也是协程别称“轻量级线程”的原因。
一个线程中可以有任意多个协程,但某一时刻只能有一个协程在运行,多个协程分享该线程分配到的计算机资源。
多数语言在语法层面并不直接支持协程,而是通过库的方式支持,但用库的方式支持的功能也并不完整,比如仅仅提供协程的创建、销毁与切换等能力。如果在这样的轻量级线程中调用一个同步 IO 操作,比如网络通信、本地文件读写,都会阻塞其他的并发执行轻量级线程,从而无法真正达到轻量级线程本身期望达到的目标。
在协程中,调用一个任务就像调用一个函数一样,消耗的系统资源最少!但能达到进程、线程并发相同的效果。
在一次并发任务中,进程、线程、协程均可以实现。从系统资源消耗的角度出发来看,进程相当多,线程次之,协程最少。
创建Goroutine
main goroutine
。新的goroutine会用go语句来创建。而go语言的并发设计,让我们很轻松就可以达成这一目的。实例:
package main
import (
"fmt"
"time"
)
//子goroutine
func newTask() {
i := 0
for {
i++
fmt.Println("new Goroutine : i =", i)
time.Sleep(1 * time.Second)
}
}
//主goroutine
func main() {
//创建一个go程,去执行newTask流程
go newTask()
i := 0
for {
i++
fmt.Println("main Goroutine : i =", i)
time.Sleep(1 * time.Second)
}
}
使用匿名方法创建goroutine:
func () {
//函数体
}() //匿名函数{}后面加()代表直接调用
实例:
package main
import (
"fmt"
"time"
)
// 主goroutine
func main() {
//用go创建承载一个形参为空,返回值为空的一个函数
go func() {
defer fmt.Println("A.defer")
func() {
defer fmt.Println("B.defer")
//退出当前goroutine
fmt.Println("B")
}()
fmt.Println("A")
}()
//死循环
for {
time.Sleep(1 * time.Second)
}
}
Goexit函数
runtime.Goexit()
package main
import (
"fmt"
"runtime"
"time"
)
// 主goroutine
func main() {
//用go创建承载一个形参为空,返回值为空的一个函数
go func() {
defer fmt.Println("A.defer")
func() {
defer fmt.Println("B.defer")
//退出当前goroutine
runtime.Goexit()
fmt.Println("B")
}()
fmt.Println("A")
}()
//死循环
for {
time.Sleep(1 * time.Second)
}
}
和map类似,channel也是一个对应make创建的底层数据结构的引用。
当我们复制一个channel或用于函数参数传递时,我们只是拷贝了一个channel引用,因此调用者和被调用者将引用同一个channel对象。和其它的引用类型一样,channel的零值也是nil。
定义一个channel时,也需要定义发送到channel的值的类型。channel可以使用内置的make()函数来创建
chan
是创建channel所需使用的关键字。Type
代表指定channel收发数据的类型。
make(chan Type) //等价于make(chan Type, 0)
make(chan Type, capacity)
<-
来接收和发送数据,发送和接收数据语法: channel <- value //发送value到channel
<-channel //接收并将其丢弃
x := <-channel //从channel中接收数据,并赋值给x
x, ok := <-channel //功能同上,同时检查通道是否已关闭或者是否为空
<-
和chan之间不能有空格实例1:
package main
import (
"fmt"
)
func main() {
c := make(chan int)
go func() {
defer fmt.Println("子go程结束")
fmt.Println("子go程正在运行……")
c <- 666 //666发送到c
}()
num := <-c //从c中接收数据,并赋值给num
fmt.Println("num = ", num)
fmt.Println("main go程结束")
}
无缓冲的通道(unbuffered channel)是指在接收前没有能力保存任何数据值的通道。
下图展示两个 goroutine 如何利用无缓冲的通道来共享一个值:
无缓冲的channel创建格式:
make(chan Type) //等价于make(chan Type, 0)
如果没有指定缓冲区容量,那么该通道就是同步的,因此会阻塞到发送者准备好发送和接收者准备好接收。
实例:
package channel_go
import (
"fmt"
"time"
)
func Channel() {
//定义一个channel
c := make(chan int)
go func() {
defer fmt.Println("goroutine1 结束")
fmt.Println("goroutine1 正在运行...")
time.Sleep(3 * time.Second)
c <- 777 //将777发送给c
}()
go func() {
defer fmt.Println("goroutine2 结束")
fmt.Println("goroutine2 正在运行...")
num := <-c //从c中接收数据,并赋值给num
fmt.Println("goroutine2 接收到数据, num = ", num)
}()
for {
time.Sleep(1 * time.Second)
}
}
有缓冲的通道(buffered channel)是一种在被接收前能存储一个或者多个数据值的通道。
有缓冲的channel创建格式:
make(chan Type, capacity)
len(ch)
求取缓冲区中剩余元素个数, cap(ch)
求取缓冲区元素容量大小。
len(ch)
返回channel中已有的元素个数cap(ch)
返回channel的最大容量实例1:
package channel_go
import (
"fmt"
"time"
)
func NonBlockChannel() {
c := make(chan int, 3)
fmt.Println("len =", len(c), "cap =", cap(c))
go func() {
defer fmt.Println("子goroutine结束")
for i := 0; i < 10; i++ {
if len(c) == cap(c) {
fmt.Println("子goroutine发送阻塞")
}
c <- i
fmt.Println("子goroutine正在运行,发送的元素:", i, "len =", len(c), "cap =", cap(c))
time.Sleep(1 * time.Second)
}
}()
for i := 0; i < 10; i++ {
if len(c) == 0 {
fmt.Println("主goroutine接收阻塞")
}
num := <-c
fmt.Println("主接收的元素:", num, "len =", len(c), "cap =", cap(c))
time.Sleep(2 * time.Second)
}
}
如果发送者知道,没有更多的值需要发送到channel的话,那么让接收者也能及时知道没有多余的值可接收将是有用的,因为接收者可以停止不必要的接收等待。这可以通过内置的close函数来关闭channel实现。
;
后对表达式的值进行条件判断if data, ok := <-c; ok {
fmt.Println("recrive data :", data)
}
实例1:
package channel_go
import (
"fmt"
"time"
)
func CloseChannel() {
c := make(chan int)
go func() {
for i := 0; i < 5; i++ {
c <- i
}
//close可以关闭一个channel
//close(c)
}()
for {
//ok返回的是channel是否关闭
if data, ok := <-c; ok {
fmt.Println("recrive data :", data)
} else {
break
}
}
fmt.Println("main finished")
}
实例2:
package channel_go
import (
"fmt"
"time"
)
func CloseChannel() {
c := make(chan int)
go func() {
for i := 0; i < 5; i++ {
c <- i
}
//close可以关闭一个channel
//close(c)
}()
for {
//ok返回的是channel是否关闭
if data, ok := <-c; ok {
fmt.Println("recrive data :", data)
} else {
break
}
}
fmt.Println("main finished")
}
总结:
可以使用 range 来迭代不断操作channel:
package channel_go
import (
"fmt"
)
func CloseChannel() {
c := make(chan int)
go func() {
for i := 0; i < 5; i++ {
c <- i
}
//close可以关闭一个channel
//close(c)
}()
//range会阻塞等待c中的数据,可以使用range来迭代不断操作channel
for data := range c {
fmt.Println("recrive data :", data)
}
fmt.Println("main finished")
}
单流程下一个goroutine只能监控一个channel状态,select可以完成监控多个channel的状态
select {
case <- chan1:
//如果chan1成功读取到数据,则进行该case处理语句
case chan2 <- 1:
//如果成功向chan2写入数据,则进行该case处理语句
default:
//如果上面都没有成功,则进入default处理流程
}
实例:
package channel_go
import (
"fmt"
)
func fibonacii(c, quit chan int) {
x, y := 1, 1
for {
select {
case c <- x:
//如果c可写,则该case就会进来,并且x会写入c
pre_x := x
x = y
y = pre_x + y
case <-quit:
//如果quit可读,就退出
fmt.Println("quit")
return
}
}
}
func SelectAndChannel() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 5; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
//main
fibonacii(c, quit)
}
默认情况下,通道channel是双向的,也就是,既可以往里面发送数据也可以同里面接收数据。
但是,我们经常见一个通道作为参数进行传递而只希望对方是单向使用的,要么只让它发送数据,要么只让它接收数据,这时候我们可以指定通道的方向。
var ch1 chan int // ch1是一个正常的channel,是双向的
var ch2 chan<- float64 // ch2是单向channel,只用于写float64数据
var ch3 <-chan int // ch3是单向channel,只用于读int数据
chan<-
表示数据进入管道,要把数据写进管道,对于调用者就是输出。<-chan
表示数据从管道出来,对于调用者就是得到管道的数据,当然就是输入。 c := make(chan int, 3)
var send chan<- int = c // send-only
var recv <-chan int = c // receive-only
send <- 1
//<-send //invalid operation: <-send (receive from send-only type chan<- int)
<-recv
//recv <- 2 //invalid operation: recv <- 2 (send to receive-only type <-chan int)
//不能将单向 channel 转换为普通 channel
d1 := (chan int)(send) //cannot convert send (type chan<- int) to type chan int
d2 := (chan int)(recv) //cannot convert recv (type <-chan int) to type chan int
实例:
package channel_go
import (
"fmt"
)
// chan<- 只写
func counter(out chan<- int) {
defer close(out)
for i := 0; i < 5; i++ {
out <- i //如果对方不读,会阻塞
}
}
// <-chan 只读
func printer(in <-chan int) {
for num := range in {
fmt.Println(num)
}
}
func SingleChannel() {
c := make(chan int) //读写
go counter(c) //生产者
printer(c) //消费者
fmt.Println("finished")
}