go中channel简单使用

channel是go语言在语言级别提供的goroutine间的通信机制。我们可以使用channel在两个或者多个goroutine之间传递信息。channel是进程内的通信。

channel分为带缓冲的以及不带缓冲的。

ch:=make(chan int )创建一个不带缓冲的channel。ch:=make(chan int,2,5)创建一个带缓冲的channel,其中len(ch)是2,cap(ch)是5.

1.不带缓冲的channel使用:

package main

import (
"fmt"
"time"

"runtime"
)

func main() {

runtime.GOMAXPROCS(runtime.NumCPU())
ch := make(chan int)
go ff(ch)
fmt.Println("1", time.Now())
ch <- 2
fmt.Println("2", time.Now())
fmt.Println("main end")
fmt.Println("5", time.Now())
}

func ff(ch chan int) {
fmt.Println("3", time.Now())
time.Sleep(2 * time.Second)
<-ch
fmt.Println("ff end")
fmt.Println("4", time.Now())
}

输出:

1 2016-05-12 12:50:02.4098584 +0800 CST
3 2016-05-12 12:50:02.4118585 +0800 CST
ff end
4 2016-05-12 12:50:04.4149731 +0800 CST
2 2016-05-12 12:50:04.4149731 +0800 CST
main end

5 2016-05-12 12:50:04.4149731 +0800 CST

根据时间可以分析一下程序的工作流程。首先main函数运行到ch<-2时候就阻塞了。不带缓冲的channel接收数据后会阻塞,直到从这个channel中读取数据后,阻塞的程序才恢复。等到ff函数运行到<-ch后,main函数才继续运行。这是ff函数也在运行。从打印的时间可以看出两个函数是并发的。


package main


import (
"fmt"
"time"

"runtime"
)


func main() {

runtime.GOMAXPROCS(runtime.NumCPU())
ch := make(chan int)
go ff(ch)
fmt.Println("1", time.Now())
<-ch
fmt.Println("2", time.Now())
fmt.Println("main end")
fmt.Println("5", time.Now())
}


func ff(ch chan int) {
fmt.Println("3", time.Now())
time.Sleep(2 * time.Second)
ch <- 2
fmt.Println("ff end")
fmt.Println("4", time.Now())
}

输出: 1 2016-05-12 13:03:00.3583545 +0800 CST
3 2016-05-12 13:03:00.3603546 +0800 CST
ff end
4 2016-05-12 13:03:02.3634692 +0800 CST
2 2016-05-12 13:03:02.3634692 +0800 CST
main end
5 2016-05-12 13:03:02.3634692 +0800 CST

根据打印的时间信息可以分析,main函数在执行到<-ch时,阻塞。不带缓冲的channel从中读取数据时会阻塞,直到向其中写入数据,阻塞的程序才会继续执行。


2.带缓冲的channel使用:

package main


import (
"fmt"
"time"

"runtime"
)


func main() {

runtime.GOMAXPROCS(runtime.NumCPU())
ch := make(chan int, 2)
go ff(ch)
fmt.Println("1", time.Now())
ch <- 2
fmt.Println("2", time.Now())
fmt.Println("main end")
fmt.Println("5", time.Now())
}


func ff(ch chan int) {
fmt.Println("3", time.Now())
time.Sleep(2 * time.Second)
<-ch
fmt.Println("ff end")
fmt.Println("4", time.Now())
}

输出:1 2016-05-12 13:07:27.0666093 +0800 CST
2 2016-05-12 13:07:27.0716096 +0800 CST
main end
5 2016-05-12 13:07:27.0716096 +0800 CST

根据打印信息可以分析,main函数在执行到ch<-2的时候,没有阻塞,而是继续执行。那么为什么ff函数没有来得及运行fmt.Println("3", time.Now())这条程序呢?

从上面不带channel的程序可以看出,fmt.Println("3", time.Now())这句话运行的时间比fmt.Println("1", time.Now())这句话运行的时间要晚。晚多少时间和cpu有关。我们可以先不管。可以做出假设,ff函数里面的fmt.Println("3", time.Now())还没来得及执行,main函数就执行完了,程序就退出了。在这个过程中开的goroutine都不会执行,直接取消。

那么,怎么证明上面那段程序ff没有执行是因为main结束了呢?可以在mian函数最后等待一小会。

修改上面的程序如下:

package main

import (
"fmt"
"time"

"runtime"
)

func main() {

runtime.GOMAXPROCS(runtime.NumCPU())
ch := make(chan int, 2)
go ff(ch)
fmt.Println("1", time.Now())
ch <- 2
fmt.Println("2", time.Now())
fmt.Println("main end")
fmt.Println("5", time.Now())
time.Sleep(1 * time.Second)
}

func ff(ch chan int) {
fmt.Println("3", time.Now())
time.Sleep(2 * time.Second)
<-ch
fmt.Println("ff end")
fmt.Println("4", time.Now())
}

程序输出:1 2016-05-12 13:17:44.0658997 +0800 CST
2 2016-05-12 13:17:44.0708999 +0800 CST
main end
5 2016-05-12 13:17:44.0708999 +0800 CST
3 2016-05-12 13:17:44.0678998 +0800 CST

可以看到,main最后等待了一秒时间,然后ff函数来得及执行fmt.Println("3", time.Now())。但是在ff函数执行time.Sleep(2 * time.Second)等待两秒的时候,mian已经结束了。

更改一下channel读取和写入的顺序,如下:

package main

import (
"fmt"
"time"

"runtime"
)

func main() {

runtime.GOMAXPROCS(runtime.NumCPU())
ch := make(chan int, 2)
go ff(ch)
fmt.Println("1", time.Now())
<-ch
fmt.Println("2", time.Now())
fmt.Println("main end")
fmt.Println("5", time.Now())


}

func ff(ch chan int) {
fmt.Println("3", time.Now())
time.Sleep(2 * time.Second)
ch <- 2
fmt.Println("ff end")
fmt.Println("4", time.Now())
}

输出:1 2016-05-12 13:20:06.0520208 +0800 CST
3 2016-05-12 13:20:06.055021 +0800 CST
ff end
4 2016-05-12 13:20:08.0571355 +0800 CST
2 2016-05-12 13:20:08.0571355 +0800 CST
main end
5 2016-05-12 13:20:08.0571355 +0800 CST

根据打印信息可以分析,main函数在执行到<-ch这句话是阻塞,带缓冲的channel如果里面没有数据,从中读取的话会阻塞程序的运行。等到ff函数执行到ch <- 2时,main函数才从阻塞中恢复。


channel切片用例:

package main

import (
"fmt"
"time"

"runtime"
)

func main() {

runtime.GOMAXPROCS(runtime.NumCPU())
chs := make([]chan int, 2)
chs[0] = make(chan int)
chs[1] = make(chan int)
go ff(chs[0])
go ff(chs[1])
fmt.Println("1", time.Now())
for _, v := range chs {
fmt.Println(v)
}
fmt.Println("main end")
fmt.Println("5", time.Now())
}
func ff(ch chan int) {
fmt.Println("ff end")
ch <- 1
}

输出:ff end
ff end
1 2016-05-12 18:40:47.5375708 +0800 CST
0x1165a1c0
0x1165a2c0
main end
5 2016-05-12 18:40:47.5425711 +0800 CST


使用range对channel操作时要注意。

package main


import (
"fmt"
"time"

"runtime"
)


func main() {

runtime.GOMAXPROCS(runtime.NumCPU())
chs := make(chan int)
go ff(chs)
fmt.Println("1", time.Now())
for v := range chs {
fmt.Println(v)
}
fmt.Println("main end")
fmt.Println("5", time.Now())
}
func ff(ch chan int) {
fmt.Println("ff end")
ch <- 1
}

输出: ff end
1 2016-05-12 18:44:27.3991462 +0800 CST
1

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
F:/ccs/test.go:12 +0x169
exit status 2

exit status 1


程序修改一下,对于单个不带缓冲的channel使用range时,channel需要close。

package main


import (
"fmt"
"time"

"runtime"
)


func main() {

runtime.GOMAXPROCS(runtime.NumCPU())
chs := make(chan int)
go ff(chs)
fmt.Println("1", time.Now())
for v := range chs {
fmt.Println(v)
}
fmt.Println("main end")
fmt.Println("5", time.Now())
}
func ff(ch chan int) {
fmt.Println("ff end")
ch <- 1
close(ch)
}

输出: 1 2016-05-12 18:46:47.4581571 +0800 CST
ff end
1
main end
5 2016-05-12 18:46:47.4641574 +0800 CST

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Golang并发编程使用goroutine和channel实现。Goroutine是一种轻量级的线程,可以在同一进程内并发地运行多个任务,而不会造成线程上下文切换的开销。Channel是一种特殊的数据类型,用于在不同的goroutine之间传递数据。 使用goroutine非常简单,只需要在函数调用前加上关键字go即可启动一个goroutine。例如: ``` func main() { go func() { // 这里是一个新的goroutine }() // 这里是主goroutine } ``` 使用channel也很简单,可以使用make函数创建一个channel,然后使用<-操作符发送或接收数据。例如: ``` ch := make(chan int) go func() { ch <- 42 // 发送数据 }() x := <-ch // 接收数据 ``` 在Golangchannel的发送和接收操作是阻塞的。当发送者向一个已满的channel发送数据时,发送操作会被阻塞,直到有接收者从该channel接收数据。同样地,当接收者从一个空的channel接收数据时,接收操作也会被阻塞,直到有发送者向该channel发送数据。 除了基本的发送和接收操作外,channel还支持以下操作: - 关闭通道:使用close函数关闭通道。关闭通道后,无法再发送数据,但可以继续接收数据。 - 判断通道是否已关闭:使用_, ok := <-ch语句判断通道是否已关闭。 - 使用带缓冲的通道:使用make函数创建一个带缓冲的通道,例如ch := make(chan int, 10)。带缓冲的通道可以存储一定数量的数据,当缓冲区已满时,发送操作会阻塞,直到有接收者从该通道接收数据。 通过goroutine和channel的组合,Golang实现了高效的并发编程,这也是Golang的一大特色。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值