go语言之并发编程channel

zhanghongfeng zhanghongfeng     2023-01-30     379

关键词:

单向channel:

单向通道可分为发送通道和接收通道。但是无论哪一种单向通道,都不应该出现在变量的声明中,假如初始化了这样一个变量

var uselessChan chan <- int =make(chan <- int,10)

这样一个变量该如何使用呢,这样一个只进不出的通道没有什么实际意义。那么这种单向通道的应用场景在什么地方呢。我们可以用这种变换来约束对通道的使用方式。比如下面的这种声明

func Notify(c chan <- os.Signal, sig… os.Signal)

该函数的第一个参数的类型是发送通道类型。从参数的声明来看,调用它的程序应该传入一个只能发送而不能接收的通道。但是实际上应该传入的双向通道,Go会依据该参数的声明,自动把它转换成一个单向通道。Notify函数中的代码只能向通道c发送数据,而不能从它那里接收数据,在该函数中从通道c接收数据会导致编译错误。但是在函数之外不存在这个约束。

 

现在对SignalNotifier接口的声明稍作改变,如下:

type SignalNotifier interface

    Notify(sig…os.Signal) <- chan os.Signal

现在这个声明放在了函数外面,这种实现方法说明Notify方法的调用只能从作为结果的通道中接收元素值,而不能向其发送元素值。来看一个单向通道的例子

 

var strChan=make(chan string,3)

var mapChan=make(chan map[string]int,1)

 

func receive(strChan <- chan string,syncChan1 <- chan struct,syncChan2 chan <- struct)

        <-syncChan1

        fmt.Println("Received a sync signal and wait a second...[receiver]")

        time.Sleep(time.Second)

        for

                 if elem,ok:=<-strChan;ok

                         fmt.Println("Received:",elem,"[receiver]")

                 else

                         break

                

       

        fmt.Println("stopped.[receiver]")

        syncChan2 <- struct

 

func send(strChan chan <- string,syncChan1 chan <- struct,syncChan2 chan <- struct)

        for _,elem:=range[]string"a","b","c","d","e"

                 strChan <- elem

                 fmt.Println("sent:",elem,"[sender]")

                 if elem == "c"

                         syncChan1 <- struct

                         fmt.Println("sent a sync signal.[sender]")

                

       

        fmt.Println("wait 2 seconds...[sender]")

        time.Sleep(time.Second*2)

        close(strChan)

        syncChan2 <- struct

 

func main()

        syncChan1:=make(chan struct,1)

        syncChan2:=make(chan struct,2)

        go receive(strChan,syncChan1,syncChan2)

        go send(strChan,syncChan1,syncChan2)

        <-syncChan2

        <-syncChan2

receive函数只能对strChan和syncChan1通道进行接收操作。而send函数只能对这2个通道进行发送操作。区别点在于chan 和 <-的位置。chan <- 表明是接收通道。<- chan表明是发送通道。运行结果如下:

sent: a [sender]

sent: b [sender]

sent: c [sender]

sent a sync signal.[sender]

Received a sync signal and wait a second...[receiver]

sent: d [sender]

Received: a [receiver]

Received: b [receiver]

Received: c [receiver]

Received: d [receiver]

Received: e [receiver]

sent: e [sender]

wait 2 seconds...[sender]

stopped.[receiver]

 

非缓冲channel

如果在初始化一个通道时将其容量设置为0或者直接忽略对容量的设置。就会使该通道变成一个非缓冲通道。和异步的方式不同,非缓冲通道只能同步的传递元素值

1 向此类通道发送元素值的操作会被阻塞。直到至少有一个针对通道的接收操作进行为止。该接收操作会首先得到元素的副本,然后再唤醒发送方所在的goroutine之后返回。也就是说,这是的接收操作会在对应的发送操作完成之前完成。

2 从此类通道接收元素值的操作会被阻塞,直到至少有一个针对该通道的发送操作进行为止。发送操作会直接把元素值赋值给接收方,然后再唤醒接收方所在的goroutine之后返回。这时的发送操作会在对应的接收操作之前完成。

func main()

        sendingInterval:=time.Second

        receptionInterval:=time.Second*2

        intChan:=make(chan int,0)

        go func()

                 var ts0,ts1 int64

                 for i:=1;i<=5;i++

                         intChan <- i

                         ts1=time.Now().Unix()

                         if ts0 == 0

                                  fmt.Println("sent:",i)

                         else

                                  fmt.Println("Sent:",i,"[interval:",ts1-ts0,"] ")

                        

                         ts0=time.Now().Unix()

                         time.Sleep(sendingInterval)

                

                 close(intChan)

        ()

        var ts0,ts1 int64

        Loop:

        for

                 select

                 case v,ok:=<-intChan:

                         if !ok

                                  break Loop

                        

                     ts1=time.Now().Unix()

                         if ts0 == 0

                                  fmt.Println("receive:",v)

                         else

                                  fmt.Println("receive:",v,"[interval:",ts1-ts0,"] ")

                        

                

                 ts0=time.Now().Unix()

                 time.Sleep(receptionInterval)

       

        fmt.Println("End.")

运行结果:

sent: 1

receive: 1

receive: 2 [interval: 2 ]

 

Sent: 2 [interval: 2 ]

 

Sent: 3 [interval: 2 ]

 

receive: 3 [interval: 2 ]

 

receive: 4 [interval: 2 ]

 

Sent: 4 [interval: 2 ]

 

receive: 5 [interval: 2 ]

 

Sent: 5 [interval: 2 ]

 

End.

可以看到发送操作和接收操作都与receptioninterval的间隔一致。如果把sendingInterval改成time.Second*4. 则结果如下:发送操作和接收操作都与sendingInterval的间隔一致

sent: 1

receive: 1

Sent: 2 [interval: 4 ]

 

receive: 2 [interval: 4 ]

 

Sent: 3 [interval: 4 ]

 

receive: 3 [interval: 4 ]

 

Sent: 4 [interval: 4 ]

 

receive: 4 [interval: 4 ]

 

Sent: 5 [interval: 4 ]

 

receive: 5 [interval: 4 ]

 

End.

go语言学习之旅--并发编程

...time包golang并发编程之Mutex互斥锁实现同步golang并发编程之channel的遍历golang并发编程之selectswitchgolang并发编程之Timergolang并发编程之原子变量的引入golang并发编程之原子操作详 查看详情

go语言学习之旅--并发编程

...time包golang并发编程之Mutex互斥锁实现同步golang并发编程之channel的遍历golang并发编程之selectswitchgolang并发编程之Timergolang并发编程之原子变量的引入golang并发编程之原子操作详解golang并发编程之协程https://blog.csdn.net/guolianggsta/article... 查看详情

2021-go语言并发编程

...-协程6、并发简略-对比并发模型7、Go语言goroutine8、Go语言channel9、Go语言channel的操作10、Go语言channel的应用11、Go语言select12、Go协程调度模型-113、Go协程调度模型-214、Go语言同步2 查看详情

2021-go语言并发编程

...-协程6、并发简略-对比并发模型7、Go语言goroutine8、Go语言channel9、Go语言channel的操作10、Go语言channel的应用11、Go语言select12、Go协程调度模型-113、Go协程调度模型-214、Go语言同步2-等待组15、Go语言同步3-条件变量16、Go语言同步4-sync... 查看详情

go并发编程

channel  go语言提供的消息通信机制被称为channel.  "不要通过共享内存来通信,而应该通过通信来共享内存".  channel是go语言在语言级别提供的goroutine之间的通信方式.是类型相关的,一个channel只能传递一种类型的值.这个类型... 查看详情

golang并发编程之channel(代码片段)

一、概念channel是golang语言级别提供的协程(goroutine)之间的通信方式。goroutine运行在相同的地址空间,因此访问共享内存必须做好同步。那么goroutine之间如何进行数据的通信呢,Go提供了一个很好的通信机制channel。channel可以与Unixs... 查看详情

go语言基础之并发(代码片段)

...调度完成,而线程是由操作系统调度完成。Go语言还提供channel在多个goroutine间进行通信。goroutine和channel是Go语言秉承的CSP(CommunicatingSequentialProcess)并发模式的重要实现基础。goroutine在java/c++中我们要实现并发编程的时候,我们... 查看详情

go语言基础之并发(代码片段)

...调度完成,而线程是由操作系统调度完成。Go语言还提供channel在多个goroutine间进行通信。goroutine和channel是Go语言秉承的CSP(CommunicatingSequentialProcess)并发模式的重要实现基础。goroutine在java/c++中我们要实现并发编程的时候,我们... 查看详情

go语言自学系列|golang并发编程之channel的遍历(代码片段)

视频来源:B站《golang入门到项目实战[2021最新Go语言教程,没有废话,纯干货!持续更新中...]》一边学习一边整理老师的课程内容及试验笔记,并与大家分享,请移步至知乎网站,谢谢支持!附上... 查看详情

19.go语言基础之并发(代码片段)

...)调度完成,而线程是由操作系统调度完成。Go语言还提供channel在多个goroutine间进行通信。goroutine和channel是Go语言秉承的CSP(CommunicationSequentialProcess)并发模式的重要实现基础。1.2goroutine在java/Python中,我们实现并发编程的时候,... 查看详情

go并发编程基础-channel(代码片段)

协程(Goroutine)Go语言中没有线程的概念,只有协程,也称为goroutine。相比线程来说,协程更加轻量,一个程序可以随意启动成千上万个goroutine。goroutine被Goruntime所调度,这一点和线程不一样。也就是说,Go语言的并发是由Go自己... 查看详情

并发编程如何用channel解决并发问题?(代码片段)

...河系】期待你的关注。未来大家一起加油啊~前言什么是Channel?在Go语言基础中应该就学过Channel,那个时候应该都认为只是一个基础类型,是一个管道一样类似的东西,方便快速读写操作,但是Channel在并发中... 查看详情

go语言并发编程

通道(channel)单纯地将函数并发执行是没有意义的。函数与函数间需要交换数据才能体现并发执行函数的意义。虽然可以使用共享内存进行数据交换,但是共享内存在不同的goroutine中容易发生竞态问题。为了保证数据交换的正... 查看详情

go语言学习笔记—进阶—并发编程:通道(channel)——使用channel收发数据(代码片段)

创建channel之后,我们可以使用channel进行数据收发操作。使用channel发送(填充)数据使用特殊的操作符<-,把数据通过channel发送。格式通道变量<-值通道变量是上文通过make创建的通道实例;值可以是变量... 查看详情

09.go语言并发(代码片段)

...调度完成,而线程是由操作系统调度完成。Go语言还提供channel在多个goroutine间进行通信。goroutine和channel是Go语言秉承的CSP(CommunicatingSequentialProcess)并发模式的重要实现基础。本章中,将详细为大家讲解goroutine和channel及相关特... 查看详情

go语言系列之并发编程(代码片段)

Go语言中的并发编程并发与并行并发:同一时间段内执行多个任务(你在用微信和两个女朋友聊天)。并行:同一时刻执行多个任务(你和你朋友都在用微信和女朋友聊天)。Go语言的并发通过goroutine实现。goroutine类似于线程,... 查看详情

go语言自学系列|golang并发编程之selectswitch(代码片段)

...itch语句,用于处理异步IO操作。select会监听case语句中channel的读写操作,当case中channel读写操作为非阻塞状态(即能读写)时,将会触发相应的动作。select的case语句必须是一个channel的操作select的default子句总是... 查看详情

go语言基础之并发goroutinechan(代码片段)

...uot;fmt""sync")varwgsync.WaitGroupfuncmain()wg.Add(2)gonobufChannel()//不带缓冲区的初始化gobufChannel()//有缓冲区的通道wg.Wait()//不带缓冲区的初始化funcnobufChannel()deferwg.Done()channel1:=make(chanint)gofunc()x:=<-channel1fmt.Println("channel里取... 查看详情