go语言实现并发
通过go 实现并发操作
func say(s string) {
for i := 0; i < 5; i++ {
fmt.Println(s)
}
}
func main(){
go say("hello")
say("world")
}
执行上述代码,会间隔执行say方法
通过channel实现并发数据间的通信
channel的创建必须通过make方式实现
ch := make(chan int)
channel通过操作符<-接收和发送数据
ch <- value //发送value到channel ch
value := <-ch //从channel ch中获取数据
下面开始是此节的重点内容,废话不多说先上代码
代码1:
func sum(a []int, c chan int) {
fmt.Println("开始往channel中写数据", a)
total := 0
for _, v := range a {
total += v
}
c <- total
fmt.Println("单次写数据处理完成")
}
func main() {
a := []int{1, 2, 3, 4}
c := make(chan int)
fmt.Println("write")
sum(a[:len(a)/2], c)
sum(a[len(a)/2:], c)
fmt.Println("write end")
}
控制台输出:
write
开始往channel中写数据 [1 2]
单次写数据处理完成
开始往channel中写数据 [3 4]
fatal error: all goroutines are asleep - deadlock!
程序出现错误,因为我们创建一个未指定缓冲大小的channel(当未指定缓冲大小时,channel默认为0),
我们调用两次channel的写操作,出现了阻塞
代码2:
func sum(a []int, c chan int) {
fmt.Println("开始往channel中写数据", a)
total := 0
for _, v := range a {
total += v
}
fmt.Println("单次写数据处理完成")
c <- total
}
func main() {
a := []int{1, 2, 3, 4}
c := make(chan int, 2)//这里我们指定channel的缓冲为2
fmt.Println("write")
sum(a[:len(a)/2], c)
sum(a[len(a)/2:], c)
fmt.Println("write end")
}
控制台输出:
write
开始往channel中写数据 [1 2]
单次写数据处理完成
开始往channel中写数据 [3 4]
单次写数据处理完成
write end
程序正常结束,因为我们指定了channel的缓冲大小,所以可以进行两次写操作
代码3:
func sum(a []int, c chan int) {
fmt.Println("开始往channel中写数据", a)
total := 0
for _, v := range a {
total += v
}
fmt.Println("单次写数据处理完成")
c <- total
}
func main() {
a := []int{1, 2, 3, 4}
c := make(chan int)//如果我们不指定channel的缓冲大小如何实现两次写操作呢
fmt.Println("write")
go sum(a[:len(a)/2], c)
go sum(a[len(a)/2:], c)
fmt.Println("write end")
}
控制台输出:
write
write end
程序正常结束,这是因为我们使用并发调用调用sum方法
代码4:
var (
wg sync.WaitGroup
)
func sum(a []int, c chan int) {
fmt.Println("开始往channel中写数据", a)
total := 0
for _, v := range a {
total += v
}
fmt.Println("单次写数据处理完成")
c <- total
wg.Done()
}
func main() {
wg.Add(2)
a := []int{1, 2, 3, 4}
c := make(chan int)
fmt.Println("write")
go sum(a[:len(a)/2], c)
go sum(a[len(a)/2:], c)
fmt.Println("write end")
wg.Wait()
}
控制台输出:
fatal error: all goroutines are asleep - deadlock!
write
write end
程序异常,我们在主线程采用WaitGroup保持主线程不被销毁,这样异步线程就有时间进行处理,造成chan阻塞,代码4未发生阻塞,是因为主线程立刻就结束
代码5:
func sum(a []int, c chan int) {
fmt.Println("开始往channel中写数据", a)
total := 0
for _, v := range a {
total += v
}
fmt.Println("单次写数据处理完成")
c <- total
}
func main() {
a := []int{1, 2, 3, 4}
c := make(chan int,1)//我们指定channel的缓冲大小为1,但是我们调用两次写操作,其中一次为并发调用
fmt.Println("write")
go sum(a[:len(a)/2], c)
sum(a[len(a)/2:], c)
fmt.Println("write end")
fmt.Println("read x")
x := <-c
fmt.Println("x = ", x)
fmt.Println("read Y")
y := <-c
fmt.Println("y = ", y)
fmt.Println("read end")
}
控制台输出:
write
开始往channel中写数据 [3 4]
单次写数据处理完成
write end
read x
x = 7
read Y
开始往channel中写数据 [1 2]
单次写数据处理完成
y = 3
read end
程序正常结束,可以看到首先执行了main线程调用的sum方法,
然后进行一次x值的读取,取得的为main线程写入的数据,
再进行一次y值的读取,先进行了数据的写入,然后返回写入的值
代码6:
func sum(a []int, c chan int) {
fmt.Println("开始往channel中写数据", a)
total := 0
for _, v := range a {
total += v
}
fmt.Println("单次写数据处理完成")
c <- total
}
func main() {
a := []int{1, 2, 3, 4}
c := make(chan int,2)//我们指定channel的缓冲大小为2
fmt.Println("write")
sum(a[:len(a)/2], c)
sum(a[len(a)/2:], c)
fmt.Println("write end")
fmt.Println("read x")
x := <-c
fmt.Println("x = ", x)
fmt.Println("read Y")
y := <-c
fmt.Println("y = ", y)
fmt.Println("read end")
}
控制台输出:
write
开始往channel中写数据 [1 2]
单次写数据处理完成
开始往channel中写数据 [3 4]
单次写数据处理完成
write end
read x
x = 3
read Y
y = 7
read end
程序正常结束,可以看到首先执行两次main线程调用的sum方法,然后分别x,y获取两次写入的值
代码7:
func sum(a []int, c chan int) {
fmt.Println("开始往channel中写数据", a)
total := 0
for _, v := range a {
total += v
}
fmt.Println("单次写数据处理完成")
c <- total
}
func main() {
a := []int{1, 2, 3, 4}
c := make(chan int)//我们未指定channel的缓冲大小
fmt.Println("write")
go sum(a[:len(a)/2], c)
go sum(a[len(a)/2:], c)
fmt.Println("write end")
fmt.Println("read x")
x := <-c
fmt.Println("x = ", x)
fmt.Println("read Y")
y := <-c
fmt.Println("y = ", y)
fmt.Println("read end")
}
控制台输出:
write
write end
read x
开始往channel中写数据 [3 4]
单次写数据处理完成
x = 7
read Y
开始往channel中写数据 [1 2]
单次写数据处理完成
y = 3
read end
程序正常结束,可以看到我们异步调用两次sum方法,
然后main线程获取x值,输入第二次异步的日志,同时返回写入的数据,
获取y值,输入第一次异步的日志,同时返回写入的数据
通过range获取channel数据
range获取channel的数据跟普通的<-唯一的区别在于需要显形的关闭channel
close(ch)
废话不多说继续上代码
代码1:
func fibonacci(n int, c chan int) {
x, y := 1, 1
fmt.Println("开始往channel中写数据", n)
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
fmt.Println("单次写数据处理完成")
}
func main() {
ch2 := make(chan int)
fmt.Println("开始range的写操作")
go fibonacci(10, ch2)
fmt.Println("写操作结束")
fmt.Println("开始range的读操作")
for i := range ch2 {
fmt.Println(i)
}
fmt.Println("读操作结束")
}
控制台输出:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
/Users/cheyongzi/Desktop/data/project/GoDemo/routine/runtime.go:66 +0x242
开始range的写操作
写操作结束
开始range的读操作
开始接收
1
1
2
3
5
8
13
21
34
55
程序异常退出,这是因为range获取channel的数据需要显性的调用close方法关闭channel
代码1:
func fibonacci(n int, c chan int) {
x, y := 1, 1
fmt.Println("开始往channel中写数据", n)
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c)
fmt.Println("单次写数据处理完成")
}
func main() {
ch2 := make(chan int)
fmt.Println("开始range的写操作")
go fibonacci(10, ch2)//这里如果不采用异步调用,则需要在ch2定义的时候指定缓冲大小,否则会报错
fmt.Println("写操作结束")
fmt.Println("开始range的读操作")
for i := range ch2 {
fmt.Println(i)
}
fmt.Println("读操作结束")
}
控制台输出:
开始range的写操作
写操作结束
开始range的读操作
开始接收
1
1
2
3
5
8
13
21
34
55
读操作结束
程序正常运行