forked from webtao520/golang_project
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
248 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
func main(){ | ||
ch:=make(chan int) | ||
// 声明一个只能写入数据的通道类型,并赋值为ch | ||
var chSendOnly chan<-int = ch | ||
// 声明一个只能读取数据的通道类型,并赋值为ch | ||
var chRecvOnly <-chan int =ch | ||
} | ||
|
||
/** | ||
上面的例子中,chSendOnly 只能写入数据,如果尝试读取数据,将会出现如下报错: | ||
invalid operation: <-chSendOnly (receive from send-only type chan<- int) | ||
同理,chRecvOnly 也是不能写入数据的。 | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
Go语言的类型系统提供了单方向的 channel 类型,顾名思义,单向 channel 就是只能用于写入或者只能用于读取数据。 | ||
当然 channel 本身必然是同时支持读写的,否则根本没法用。 | ||
|
||
假如一个 channel 真的只能读取数据,那么它肯定只会是空的,因为你没机会往里面写数据。同理, | ||
如果一个 channel 只允许写入数据,即使写进去了,也没有丝毫意义,因为没有办法读取到里面的数据。所谓的单向 channel 概念,其实只是对 channel 的一种使用限制。 | ||
|
||
### 单向通道的声明格式 | ||
|
||
我们在将一个 channel 变量传递到一个函数时,可以通过将其指定为单向 channel 变量, | ||
从而限制该函数中可以对此 channel 的操作,比如只能往这个 channel 中写入数据,或者只能从这个 channel 读取数据。 | ||
|
||
单向 channel 变量的声明非常简单,只能写入数据的通道类型为chan<-,只能读取数据的通道类型为<-chan,格式如下: | ||
var 通道实例 chan<- 元素类型 // 只能写入数据的通道 | ||
var 通道实例 <-chan 元素类型 // 只能读取数据的通道 | ||
|
||
元素类型:通道包含的元素类型。 | ||
通道实例:声明的通道变量。 | ||
|
||
### 单向通道的使用例子 | ||
示例代码如下: | ||
+ 案例 | ||
* 1.go | ||
|
||
### time包中的单向通道 | ||
|
||
time 包中的计时器会返回一个 timer 实例,代码如下: | ||
|
||
timer := time.NewTimer(time.Second) | ||
timer的Timer类型定义如下: | ||
|
||
type Timer struct { | ||
C <-chan Time | ||
r runtimeTimer | ||
} | ||
第 2 行中 C 通道的类型就是一种只能读取的单向通道。如果此处不进行通道方向约束,一旦外部向通道写入数据,将会造成其他使用到计时器的地方逻辑产生混乱。 | ||
|
||
因此,单向通道有利于代码接口的严谨性。 | ||
|
||
|
||
### 关闭 channel | ||
|
||
关闭 channel 非常简单,直接使用Go语言内置的 close() 函数即可: | ||
close(ch) | ||
|
||
在介绍了如何关闭 channel 之后,我们就多了一个问题:如何判断一个 channel 是否已经被关闭?我们可以在读取的时候使用 | ||
|
||
### 多重返回值的方式: | ||
|
||
x, ok := <-ch | ||
|
||
这个用法与 map 中的按键获取 value 的过程比较类似,只需要看第二个 bool 返回值即可,如果返回值是 false 则表示 ch 已经被关闭。 |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// 这个示例程序展示如何用无缓冲的通道来模拟 | ||
// 2 个goroutine 间的网球比赛 | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"math/rand" | ||
"sync" | ||
"time" | ||
) | ||
|
||
// wg 用来等待程序结束 | ||
var wg sync.WaitGroup | ||
|
||
func init() { | ||
rand.Seed(time.Now().UnixNano()) | ||
} | ||
|
||
// main 是所有Go 程序的入口 | ||
func main() { | ||
// 创建一个无缓冲的通道 | ||
court := make(chan int) | ||
|
||
// 计数加 2,表示要等待两个goroutine | ||
wg.Add(2) | ||
|
||
// 启动两个选手 | ||
go player("Nadal", court) | ||
go player("Djokovic", court) | ||
|
||
// 发球 | ||
court <- 1 | ||
|
||
// 等待游戏结束 | ||
wg.Wait() | ||
} | ||
|
||
// player 模拟一个选手在打网球 | ||
func player(name string, court chan int) { | ||
// 在函数退出时调用Done 来通知main 函数工作已经完成 | ||
defer wg.Done() | ||
|
||
for { | ||
// 等待球被击打过来 | ||
ball, ok := <-court | ||
if !ok { | ||
// 如果通道被关闭,我们就赢了 | ||
fmt.Printf("Player %s Won\n", name) | ||
return | ||
} | ||
|
||
// 选随机数,然后用这个数来判断我们是否丢球 | ||
n := rand.Intn(100) | ||
if n%13 == 0 { | ||
fmt.Printf("Player %s Missed\n", name) | ||
|
||
// 关闭通道,表示我们输了 | ||
close(court) | ||
return | ||
} | ||
|
||
// 显示击球数,并将击球数加1 | ||
fmt.Printf("Player %s Hit %d\n", name, ball) | ||
ball++ | ||
|
||
// 将球打向对手 | ||
court <- ball | ||
} | ||
} | ||
|
||
|
||
|
||
/** | ||
PS D:\goLang\github\golang_project\Go语言并发\Go语言无缓冲的通道> go run 1.go | ||
Player Djokovic Hit 1 | ||
Player Nadal Hit 2 | ||
Player Djokovic Hit 3 | ||
Player Nadal Hit 4 | ||
Player Djokovic Hit 5 | ||
Player Nadal Hit 6 | ||
Player Djokovic Hit 7 | ||
Player Nadal Hit 8 | ||
Player Djokovic Missed | ||
Player Nadal Won | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// 这个示例程序展示如何用无缓冲的通道来模拟 | ||
// 4 个goroutine 间的接力比赛 | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"sync" | ||
"time" | ||
) | ||
|
||
// wg 用来等待程序结束 | ||
var wg sync.WaitGroup | ||
|
||
// main 是所有Go 程序的入口 | ||
func main() { | ||
// 创建一个无缓冲的通道 | ||
baton := make(chan int) | ||
|
||
// 为最后一位跑步者将计数加1 | ||
wg.Add(1) | ||
|
||
// 第一位跑步者持有接力棒 | ||
go Runner(baton) | ||
|
||
// 开始比赛 | ||
baton <- 1 | ||
|
||
// 等待比赛结束 | ||
wg.Wait() | ||
} | ||
|
||
// Runner 模拟接力比赛中的一位跑步者 | ||
func Runner(baton chan int) { | ||
var newRunner int | ||
|
||
// 等待接力棒 | ||
runner := <-baton | ||
|
||
// 开始绕着跑道跑步 | ||
fmt.Printf("Runner %d Running With Baton\n", runner) | ||
|
||
// 创建下一位跑步者 | ||
if runner != 4 { | ||
newRunner = runner + 1 | ||
fmt.Printf("Runner %d To The Line\n", newRunner) | ||
go Runner(baton) | ||
} | ||
|
||
// 围绕跑道跑 | ||
time.Sleep(100 * time.Millisecond) | ||
|
||
// 比赛结束了吗? | ||
if runner == 4 { | ||
fmt.Printf("Runner %d Finished, Race Over\n", runner) | ||
wg.Done() | ||
return | ||
} | ||
|
||
// 将接力棒交给下一位跑步者 | ||
fmt.Printf("Runner %d Exchange With Runner %d\n", | ||
runner, | ||
newRunner) | ||
|
||
baton <- newRunner | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
Go语言中无缓冲的通道(unbuffered channel)是指在接收前没有能力保存任何值的通道。 | ||
这种类型的通道要求发送 goroutine 和接收 goroutine 同时准备好,才能完成发送和接收操作。 | ||
|
||
如果两个 goroutine 没有同时准备好,通道会导致先执行发送或接收操作的 goroutine 阻塞等待。 | ||
这种对通道进行发送和接收的交互行为本身就是同步的。其中任意一个操作都无法离开另一个操作单独存在。 | ||
|
||
阻塞指的是由于某种原因数据没有到达,当前协程(线程)持续处于等待状态,直到条件满足才解除阻塞。 | ||
|
||
同步指的是在两个或多个协程(线程)之间,保持数据内容一致性的机制。 | ||
|
||
下图展示两个 goroutine 如何利用无缓冲的通道来共享一个值。 | ||
【示例 1】在网球比赛中,两位选手会把球在两个人之间来回传递。选手总是处在以下两种状态之一,要么在等待接球,要么将球打向对方。 | ||
可以使用两个 goroutine 来模拟网球比赛,并使用无缓冲的通道来模拟球的来回,代码如下所示。 | ||
+ 案例 | ||
* 1.go | ||
|
||
【示例 2】用不同的模式,使用无缓冲的通道,在 goroutine 之间同步数据,来模拟接力比赛。在接力比赛里,4 个跑步者围绕赛道轮流跑。 | ||
第二个、第三个和第四个跑步者要接到前一位跑步者的接力棒后才能起跑。比赛中最重要的部分是要传递接力棒,要求同步传递。在同步接力棒的时候,参与接力的两个跑步者必须在同一时刻准备好交接。代码如下所示。 | ||
+ 案例 | ||
* 2.go |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -91,6 +91,12 @@ | |
|
||
|
||
### Go语言并发 | ||
* 并发和并行的区别 | ||
* Go语言单向通道 | ||
* Go语言单向通道 | ||
* Go语言轻量级线程 | ||
* Go语言通道(chan) | ||
* Go语言无缓冲的通道 | ||
|
||
### ETCD | ||
|
||
|