一、并发编程
1.创建goroutine
func newTask() {
for {
fmt.Println("this is the newTask goroutine")
time.Sleep(time.Second)
}
}
func main() {
go newTask()//创建一个新的协程,新建一个任务
for {
fmt.Println("this is the main goroutine")
time.Sleep(time.Second)//延迟一秒,引入头文件"time"
}
}
2.goroutine先退出
如果主协程退出了,那子协程会随之一起结束
//主协程退出了,其他子协程也要跟着一起推出
go func() {
i := 0
for {
i++
fmt.Println("子协程i=", i)
time.Sleep(time.Second)
}
}()//调用匿名函数,不要忘记这个()
i := 0
for {
i++
fmt.Println("main i=", i)
time.Sleep(time.Second)
if i == 2 {
break
}
}
fmt.Println("main aaaaa")
3.主协程先退出导致子协程没有来得及调用
func main() {
go func() {
i := 0
for {
i++
fmt.Println("子协程 i=", i)
time.Sleep(time.Second)
}
}()
}
没有运行结果
4.GoSched的使用
头文件"runtime"
func main() {
go func() {
for i := 0; i < 5; i++ {
fmt.Println("hello go")
}
}()
for i := 0; i < 2; i++ {
//让出时间片,先让别的协程执行,别的协程执行完,再回来执行此协程
runtime.Gosched()
fmt.Println("hello")
}
}
5.Goexit的使用
主函数如下所示
func main() {
//创建新协程
go func() {
fmt.Println("aaaaa")
test()//调用了别的函数
fmt.Println("bbbbb")
}()
for {//写一个死循环,目的是不让主协程结束
}
}
当test()函数如下:
func test() {
defer fmt.Println("ccccc")
fmt.Println("ddddd")
}
结果:
当test()如下所示:
func test() {
defer fmt.Println("ccccc")
return//终止此函数
fmt.Println("ddddd")
}
当test()如下所示:
func test() {
defer fmt.Println("ccccc")
runtime.Goexit()//终止所在的协程,包括调用它的那个协程
fmt.Println("ddddd")
}
6.GOMAXPROCS的使用
runtime.GOMAXPROCS(1)//GOMAXPROCS的参数为指定几核进行运算
for {
go fmt.Print(1)
fmt.Print(0)
}
7.多任务资源竞争问题
//定义一个打印机,参数为字符串,按每个字符进行打印
func Printer(str string) {
for _, data := range str {//遍历字符串
fmt.Printf("%c", data)
time.Sleep(time.Second)
}
fmt.Println()
}
func person1() {
Printer("Hello")
}
func person2() {
Printer("world")
}
func main() {
//新建两个协程,代表两个人,两个人同时使用打印机
go person1()
go person2()
for {//搞一个死循环,不让主协程结束
}
}
出现资源竞争问题
8.通过channel实现同步
var ch = make(chan int)//创建一个channel全局变量(无缓冲)
func Printer(str string) {
for _, data := range str {
fmt.Printf("%c", data)
time.Sleep(time.Second)
}
fmt.Println()
}
//当Person1执行完再让person2执行,这样才不会导致打印出来乱码
func person1() {
Printer("hello")
ch <- 66//向通道中写数据,发送
}
func person2() {
<-ch//从通道中取数据,接收,当通道中没有数据的时候,阻塞
Printer("world")
}
func main() {
go person1()
go person2()
for {
}
}
9.通过channel实现同步和数据交互
//创建channel,无缓冲
ch := make(chan string)
defer fmt.Println("主协程也结束了")
go func() {
defer fmt.Println("子协程调用完毕")
for i := 0; i < 2; i++ {
fmt.Println("子协程 i=", i)
time.Sleep(time.Second)
}
ch <- "我是子协程,已经工作完毕"
}()
str := <-ch//channel中没有数据的时候,会阻塞
fmt.Println("str=", str)
10.无缓冲的channel
ch := make(chan int, 0)//创建一个无缓存的channel
fmt.Printf("len(ch)=%d,cap(ch)=%d\n", len(ch), cap(ch))//len(ch)是缓冲区剩余数据的个数,cap(ch)是缓冲区大小
go func() {//新建协程
for i := 0; i < 3; i++ {
fmt.Printf("子协程i=%d\n", i)
ch <- i//往channel写内容
}
}()
time.Sleep(time.Second)//延时2秒
for i := 0; i < 3; i++ {
num := <-ch//通道中的内容,没有内容的时候会阻塞
fmt.Println("num=", num)
}
11.有缓冲的channel
ch := make(chan int, 3)//创建一个有缓冲的channel
fmt.Printf("len(ch)=%d,cap(ch)=%d\n", len(ch), cap(ch))
go func() {//新建协程
for i := 0; i < 10; i++ {
ch <- i
fmt.Printf("子协程[%d]:len(ch)=%d,cap(ch)=%d\n", i, len(ch), cap(ch))
}
}()
time.Sleep(time.Second * 2)
for i := 0; i < 10; i++ {
num := <-ch
fmt.Println("num=", num)
}
12.关闭channel
ch := make(chan int)//创建一个无缓冲的channel
go func() {//创建一个新协程
for i := 0; i < 5; i++ {
ch <- i//往通道中写数据
}
close(ch)//如果不需要再写数据,关闭channel,关闭后仍可以读管道中的数据,但是无法向管道中写数据
}()
for {
if num, ok := <-ch; ok == true {//如果ok为true,说明管道中有数据
fmt.Println("num=", num)
} else {//管道中无数据
break
}
}
13.单向channel特性
ch := make(chan int)//创建一个channel,默认为双向
var writeCh chan<- int = ch//只能写,不能读
//双向channel能隐式转换为单向channel
writeCh <- 9//写
//<-writeCh//err
var readCh <-chan int = ch
<-readCh
//readCh<-6//err
//单向无法转换为双向channel
//var ch2 chan int=writeCh//err
14.单向channel的应用
func producer(out chan<- int) {//此通道只能写,不能读
for i := 0; i < 10; i++ {
out <- i * i
}
close(out)
}
func consumer(in <-chan int) {
for num := range in {//通道的迭代只有一个参数哦
fmt.Println("num=", num)
}
}
func main() {
ch := make(chan int)//创建一个双向通道
//生产者,生产数字,写入channel
go producer(ch)//channel传参,以引用形式传递
consumer(ch)//消费者,从channel读取内容,打印出来
}
二、计时器
1.Timer的使用
//创建一个定时器,设置时间为2秒,2秒后会往timer.C通道中写内容(当前时间)
timer := time.NewTimer(2 * time.Second)
fmt.Println("当前时间:", time.Now())
//定时结束后往timer.C中写数据,有数据了就可以读了,没有数据的时候就阻塞
t := <-timer.C
fmt.Println("t=", t)
timer := time.NewTimer(time.Second)
for {
<-timer.C
fmt.Println("时间到")
}
验证time.NewTimer()时间到了,只会响应一次
2.通过Timer实现延时功能
timer := time.NewTimer(2 * time.Second)//延时2秒后打印一句话
<-timer.C
fmt.Println("时间到")
time.Sleep(2 * time.Second)
fmt.Println("时间到")
<-time.After(2 * time.Second)
//定时2秒,阻塞2秒,2秒后产生一个时间,往channel写内容
fmt.Println("时间到")
以上程序都实现一个延时作用
3.停止和重置定时器
timer := time.NewTimer(3 * time.Second)
go func() {
<-timer.C
fmt.Println("定时器时间到,子协程开始打印")
}()
timer.Stop()
for {
}
这个程序执行是没有结果的,把定时器停了,那就不会往timer.C通道里面写了,就阻塞在for循环里了
timer := time.NewTimer(3 * time.Second)
ok := timer.Reset(time.Second)//重新设置为1秒
fmt.Println("ok=", ok)
<-timer.C
fmt.Println("时间到")
不需要等3秒了,延时就1秒
4.Ticker的使用
ticker:=time.NewTicker(time.Second)
i:=0
for{
<-ticker.C
i++
fmt.Println("i=",i)
if i==5{
ticker.Stop()
break
}
}
5.使用select构造斐波那契数列
func fibonacci(ch chan<- int, quit <-chan bool) {//ch只写,quit只读
x, y := 1, 1
for {
select {//监听数据流动
case ch <- x:
x, y = y, x+y
case flag := <-quit:
fmt.Println("flag=", flag)
return
}
}
}
func main() {
ch := make(chan int)//数字通信
quit := make(chan bool)//程序是否结束
//消费者,从channel读取内容
//新建协程
go func() {
for i := 0; i < 8; i++ {
num := <-ch
fmt.Println(num)
}
quit <- true//停止信号
}()
//生产者,产生数字,写入channel
fibonacci(ch, quit)
}
6.通过select实现超时
ch := make(chan int)
quit := make(chan bool)
go func() {
for {
select {
case num := <-ch:
fmt.Println("num=", num)
case <-time.After(3 * time.Second)://通道中3秒没有数据就会显示“超时”
fmt.Println("超时")
quit <- true
}
}
}()
for i := 0; i < 10; i++ {
ch <- i
time.Sleep(time.Second)
}
<-quit
fmt.Println("程序结束")