Go学习笔记-通道基本操作

通道(channel)完全可以与 goroutine(也可称go程)并驾齐驱。

通道类型的值本身是并发安全的,且使用十分方便。

声明:

1.第一个参数代表通道的具体类型的类型字面量;

确定通道类型的元素类型;

chan int :元素类型为int的通道类型;

chan string:元素类型为string的通道类型;

2.第二个参数可选,一个int类型的数值且大于0,表示通道的容量(通道缓存元素的个数)

容量=0时:非缓存的通道;

容量>0时:缓冲的通道;

非缓冲通道和缓冲通道的数据传递方式不同:

3.一个通道相当于一个先进先出队列。

元素值的发送和接收都要用到 <- 操作符 ,尖括号表示元素值的传输方向。

实例:

package main

import "fmt"



func main() {

ch1 := make(chan int, 3)//通道初始化,缓冲3个值

ch1 <- 2 //发送值到通道

ch1 <- 1

ch1 <- 3

elem1 := <-ch1 //从通道取值

fmt.Printf("The first element received from channel ch1: %v\n",

elem1)

}

4.对通道的发送和接收操作都有哪些基本的特性?

(1)对同一个通道,发送操作直接是互斥的,接收操作之间也是互斥的;

同一时刻,只能有一个发送操作;也只能有一个接收操作;

即:进队列时,只能一个一个进,出队列时,只能一个一个出。

这里并发执行指的是:多个代码块分别在不同的gouroutine中,有机会在同一时间执行;

细节:

    a.元素从外界进入通道时会被复制,即:进入通道的并不是接收操作符右侧的那个值,而是它的副本。

    b.元素值从通道进入外界时会被移动。第一步:生成正在通道中的这个元素的副本,并准备给接收方,第二步删除在通道中的元素值。

(2)发送操作和接收操作中,对元素值的处理都是不可分割的;

    发送操作要么还没复制元素,要么已复制完毕;(原子操作)

    接收操作在准备好元素值的副本后,一定会删除通道中原值;(原子操作)

(3)发送操作在完全完成前会被阻塞。接收操作也是如此;

    发送操作 :a.复制元素值   b.放置副本到通道内部

    在这两步完成前,发起发送操作的那个代码会被阻塞;

   在通道完成发送操作后,运行时系统会通知代码所在的goroutine ,使它去争取继续运行的机会。

   接收操作:a.复制通道内的元素; b.放置副本的接收方; c.删掉原值;

   在这三步完成前,发起接收操作的那个代码会被阻塞;

    在通道完成这些操作后,运行时系统会通知代码所在的goroutine,使它去争取继续执行的机会。

5.发送操作和接受操作什么时候可能被长时间的阻塞?

 对于缓冲通道:

如果通道已满,对于它的所有发送操作会被阻塞,直到通道中有元素被取走。

此时,通道会优先通知最早等待的goroutine,后者会再次执行发送操作。

如果通道为空,对于它的接收操作会被阻塞,直到通道中有新的元素值出现。

此时,通道会优先通知最早等待接收的goroutine,并使它再次执行接收操作。

对于非缓冲通道:

无论发送操作和接收操作,一开始执行就会被阻塞,直到配对的操作也开始执行,才会继续传递。

即:非缓冲通道在用同步的方式传递数据。只有收发双方对接上才传递。

数据是直接从发送方复制给接收方,中间不用缓冲通道做中转。

相比之下,缓冲通道在用同步方式传递数据

大多数情况下,缓冲通道会作为收发双方的中间件。但是,当发送操作发现通道为空,且正好等待的接收操作时,会直接把元素值复制给接收方。

由于错误引发的阻塞:

对于值为nil的通道,不论它的具体类型是什么,对于它的发送操作和接收操作都永久处于阻塞状态。

当只声明该类型的变量但没有使用make函数对它进行初始时,该类型变量就是nil。

示例:

package main



func main() {

// 示例1。

ch1 := make(chan int, 1)

ch1 <- 1

//ch1 <- 2 // 通道已满,因此这里会造成阻塞。



// 示例2。

ch2 := make(chan int, 1)

//elem, ok := <-ch2 // 通道已空,因此这里会造成阻塞。

//_, _ = elem, ok

ch2 <- 1



// 示例3。

var ch3 chan int

//ch3 <- 1 // 通道的值为nil,因此这里会造成永久的阻塞!

//<-ch3 // 通道的值为nil,因此这里会造成永久的阻塞!

_ = ch3

}

6.发送操作和接收操作在什么时候会引发 panic?

(1)对于一个已经初始化,但未关闭的通道来说,收发操作一定不会引发panic;

      但是通道一旦关闭,再对它做发送操作,就会引发panic。

(2)关闭一个已经关闭的通道,也会引发painic.

      当把接收表达式的结果同时赋给两个变量时,第二个变量类型一定是bool类型。它的值为false,说明通道已经关闭,并且没有元素可取了。

(3)如果通道关闭时,还有元素未取出,接收表达式第一个值,仍是通道中某一元素值,第二个结果值一定是true.

  (4) 通过接收表达式的第二个结果值,来判断通道是否关闭是可能有延时的。关闭通道通常是由发送方来做。

示例:


package main



import "fmt"



func main() {

ch1 := make(chan int, 2)

// 发送方。

go func() {

for i := 0; i < 10; i++ {

fmt.Printf("Sender: sending element %v...\n", i)

ch1 <- i

}

fmt.Println("Sender: close the channel...")

close(ch1)

}()



// 接收方。

for {

elem, ok := <-ch1

if !ok {

fmt.Println("Receiver: closed channel")

break

}

fmt.Printf("Receiver: received an element: %v\n", elem)

}



fmt.Println("End.")

}



/*

Sender: sending element 0...

Sender: sending element 1...

Sender: sending element 2...

Sender: sending element 3...

Receiver: received an element: 0

Receiver: received an element: 1

Receiver: received an element: 2

Receiver: received an element: 3

Sender: sending element 4...

Sender: sending element 5...

Sender: sending element 6...

Sender: sending element 7...

Receiver: received an element: 4

Receiver: received an element: 5

Receiver: received an element: 6

Receiver: received an element: 7

Sender: sending element 8...

Sender: sending element 9...

Receiver: received an element: 8

Receiver: received an element: 9

Sender: close the channel...

Receiver: closed channel

*/

猜你喜欢

转载自blog.csdn.net/Linzhongyilisha/article/details/84348424
今日推荐