go并发编程36——管道(channel)

channel的类型

/*
channel实现同步
*/
package main

import (
	"fmt"
	"time"
)

//创建一个管道
var ch = make(chan int)

func Print(s string) {
	for _, data := range s {
		fmt.Printf("%c", data)
		time.Sleep(time.Second)
	}
	fmt.Println("\n")
}

//目标:先打印hello 再打印go
func Person1() {
	Print("hello")

	ch <- 1 //给管道写数据(发送) "1"只是用于举例
}

func Person2() {
	<-ch //从管道取数据(接收),如果通道没有数据,会阻塞

	Print("go")
}

func main() {
	//新建两个协程
	go Person1()
	go Person2()

	for {

	}
}

通过channel实现同步和数据交互

package main

import (
	"fmt"
	"time"
)

func main() {
	//创建管道
	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 <- "q"
	}()

	str := <-ch //没有数据前,会阻塞
	fmt.Println("str = ", str)
}

无缓冲的channel

package main

import (
	"fmt"
	"time"
)

func main() {
	//创建无缓冲的通道
	ch := make(chan int, 0)

	//len(ch)缓冲区剩余数据个数,cap(ch)缓冲区大小
	fmt.Printf("len(ch) = %d, cap(ch) = %d\n", len(ch), cap(ch))

	go func() {
		for i := 0; i < 3; i++ {
			fmt.Println("子协程:i = ", i)
			ch <- i //ch中的数据没有被读出去之前会阻塞在这
		}
	}()

	time.Sleep(2 * time.Second)

	for i := 0; i < 3; i++ {
		num := <-ch
		fmt.Println("num = ", num)
	}
}

有缓冲的channel

/*
格式: make(chan type, capacity)
如果给定缓冲区容量,通道是异步的。只要缓冲区有未使用的空间用于发送数据,或者还包括
可以接收的数据,那么其通信就会无阻塞的进行
*/
package main

import (
	"fmt"
	"time"
)

func main() {
	//创建有缓冲的通道
	ch := make(chan int, 3)

	fmt.Printf("len(ch) = %d, cap(ch) = %d\n", len(ch), cap(ch)) //len(ch):0 cap(ch):3

	go func() {
		for i := 0; i < 10; i++ {
			ch <- i //ch满之后不能再写
			fmt.Printf("子协程[%d]:len(ch) = %d, cap(ch) = %d\n", i, len(ch), cap(ch))
		}
	}()

	time.Sleep(2 * time.Second)

	for i := 0; i < 10; i++ {
		num := <-ch
		fmt.Println("num = ", num)
	}
}

关闭channel

/*
1. 关闭channel后,无法向channel再发送数据(引发panic错误后导致接收立即返回0)
2. 关闭channel后,可以继续向channel接收数据
3. 对于nil channel,无论收发都会别阻塞
*/
package main

import (
	"fmt"
)

func main() {
	//创建无缓冲的通道
	ch := make(chan int, 0)

	go func() {
		for i := 0; i < 5; i++ {
			ch <- i //写数据
		}

		//不需要再写数据时,关闭channel
		close(ch) //屏蔽此行会死锁 无法跳出下面的死循环
		//ch <- 1 //关闭channel后无法再发送数据
	}()

	//方法1
	/*

		for {
				//如果ok为true,说明ch没有关闭
				num, ok := <-ch //读数据
				if ok == true {
					fmt.Println("num = ", num)
				} else {
					break
				}
			}

	*/

	//方法2
	for num := range ch {
		fmt.Println("num = ", num)
	}
}

单向channel的特性

package main

//import "fmt"

func main() {
	//创建一个channel
	ch := make(chan int)

	//双向channel可以隐式转换为单向channel
	var wCh chan<- int = ch //只写
	wCh <- 12
	//<-wCh

	ch <- 12
	var rCh <-chan int = ch //只读
	<-rCh
	//rCh <- 11

	//单向是无法转换为双向的
	//var ch2 chan int = rCh
}

单向channel的应用

package main

import "fmt"

//此通道只能写不能读
func Producer(wCh chan<- int) {
	for i := 0; i < 10; i++ {
		wCh <- i * i
	}
	close(wCh)
}

//只能读 不能写
func Consumer(rCh <-chan int) {
	for num := range rCh { //rCh没有数据会阻塞
		fmt.Println("num = ", num)
	}
}

func main() {
	//创建一个双向通道
	ch := make(chan int)

	//生产者
	go Producer(ch) //channel传参是引用传递

	//消费者
	Consumer(ch)
}

timer

package main

import (
	"fmt"
	"time"
)

func main() {
	//创建一个定时器,设置时间为2s,2s后,往time通道写数据(当前时间)
	//时间到了 timer只会响应一次
	timer := time.NewTimer(2 * time.Second)
	fmt.Println("当前时间:", time.Now())

	//2s后,往timer.C写数据,有数据后,就可以读取
	t := <-timer.C //channel没有数据前会阻塞
	fmt.Println("t = ", t)
}

通过timer实现延时

/*
延时的3种方式
sleep.go
*/
package main

import (
	"fmt"
	"time"
)

func main() {
	//方式1
	time.Sleep(2 * time.Second)

	//方式2 定时2s 阻塞2s 2s后会产生一个事件 往channel写数据
	<-time.After(2 * time.Second)

	//方式3
	//延时2s后打印一句话
	timer := time.NewTimer(2 * time.Second)
	<-timer.C //时间未到会阻塞
	fmt.Println("时间到")

}

定时器停止和重置

package main

import (
	"fmt"
	"time"
)

func main() {
	timer := time.NewTimer(3 * time.Second)
	b := timer.Reset(1 * time.Second) //定时器重置
	fmt.Println("b = ", b)

	<-timer.C
	fmt.Println("时间到") //3s后打印
}

func main1() {
	timer := time.NewTimer(3 * time.Second)

	go func() { //创建一个匿名函数作为子协程
		<-timer.C //3s过后停止阻塞 执行下面的打印
		fmt.Println("定时器时间到,子协程打印")
	}()

	timer.Stop() //停止定时器

	for { //主协程死循环

	}
}

Ticker

package main

import (
	"fmt"
	"time"
)

func main() {
	ticker := time.NewTicker(1 * time.Second)

	i := 0
	for {
		<-ticker.C
		i++
		fmt.Println("i = ", i)

		if i == 5 {
			ticker.Stop()
			break
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值