实验目的
chan相关前置知识点,请参考本文底部链接。墙裂推荐阅读。
- 假设系统内除了主线程之外,有四个线程connector,sender,receiver,heartbeat
- connector,负责建立网络连接,在连接断开的时候进行重连
- sender,负责发送数据报
- receiver,负责接收数据报
- heartbeat,负责监控心跳
- 如何启动connector
- connector需要等待ConnectorNotifier,值为1连接,值为2重连
- 建立连接之后,发送三次ConnectorConnected,通知sender, sender,receiver可以开工
var ConnectorConnected chan int = make(chan int, 3) // 已连接通知
func connector(){
.....
// 成功之后发送三次通告
ConnectorConnectedNotifier <- 1
ConnectorConnectedNotifier <- 1
ConnectorConnectedNotifier <- 1
}
- 如何启动sender, receiver,以及heartbeat
- 等待ConnectorConnectedNotifier,收到之后即可开始工作
- 等待QuitNotifier进行退出
- 分别发送SenderQuitedNotifier, ReceiverQuitedNotifier, HeartBeatQuitedNotifier告诉主线程自己已经退出
- 主线程如何工作
- 发出ConnectorNotifier让connector建立连接
- 启动connector,sender,receiver,beatheart
- 等待键盘输入ctrl+c结束程序
- 发送四次QuitNotifier,通告connector,sender,receiver,beatheart退出
- 等待connector,sender,receiver,beatheart确认退出
- 自己退出
下面这样写是不对的,因为没法保证时序(先收到ConnectorQuitedNotifier,在收到ReceiverQuitedNotifier)
<- ConnectorQuitedNotifier
<- ReceiverQuitedNotifier
<- SenderQuitedNotifier
<- HeartBeatQuitedNotifier
需要这样,乱序等待
// 等待线程退出:connector, sender, receiver, heartbeat
for i := 0; i < 4; {
select{
case <- ConnectorQuitedNotifier:
i++
case <- ReceiverQuitedNotifier:
i++
case <- SenderQuitedNotifier:
i++
case <- HeartBeatQuitedNotifier:
i++
}
}
代码实现
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
)
var ConnectorNotifier chan int = make(chan int, 2) // 连接器通知, 2个(1启动,2退出,3重连)
var ConnectorConnectedNotifier chan int = make(chan int, 3) // 已连接通知
var ReceiverNotifier chan int = make(chan int) // 接收器通知, (1启动,2退出)
var SenderNotifier chan int = make(chan int) // 发送器通知, (1启动,2退出)
var QuitNotifier chan int = make(chan int, 4) // 退出通知, 4个
var FinishNotifier chan int = make(chan int)
var ConnectorQuitedNotifier chan int = make(chan int) // 接收器通知, 1启动,2退出
var ReceiverQuitedNotifier chan int = make(chan int) // 接收器通知, 1启动,2退出
var SenderQuitedNotifier chan int = make(chan int) // 接收器通知, 1启动,2退出
var HeartBeatQuitedNotifier chan int = make(chan int) // 接收器通知, 1启动,2退出
func main(){
// 标记启动连接器
ConnectorNotifier <- 1
go Connector()
go Receiver()
go Sender()
go HeartBeat()
// 等待ctrl + c退出
fmt.Println("等待退出")
SetupCloseHandler()
<- FinishNotifier
fmt.Println("准备退出,开始清理")
// 通知线程退出:connector, sender, receiver, heartbeat
QuitNotifier <- 1
QuitNotifier <- 1
QuitNotifier <- 1
QuitNotifier <- 1
// 等待线程退出:connector, sender, receiver, heartbeat
for i := 0; i < 4; {
select{
case <- ConnectorQuitedNotifier:
i++
case <- ReceiverQuitedNotifier:
i++
case <- SenderQuitedNotifier:
i++
case <- HeartBeatQuitedNotifier:
i++
}
}
fmt.Println("主程序退出")
}
/*监听ctrl+c控制主线程退出*/
func SetupCloseHandler() {
c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
FinishNotifier <- 1
fmt.Println("\r- Ctrl+C pressed in Terminal")
}()
}
/*
连接管理器
1. 首先试着建立连接,建立连接失败则报错,3秒后重试
2. 建立连接之后,通知其他线程连接已建立
3. 收到重连消息后,则进行重连
4. 收到退出消息后则退出
*/
func Connector(){
var value int
for {
select{
case value = <-ConnectorNotifier:
if value == 1 || value == 2{
// 连接
fmt.Println("准备连接")
fmt.Println("连接成功")
ConnectorConnectedNotifier <- 1
ConnectorConnectedNotifier <- 1
ConnectorConnectedNotifier <- 1
}
case value = <-QuitNotifier:
ConnectorQuitedNotifier <- 1
fmt.Println("Connector退出")
}
}
}
func Receiver(){
<- ConnectorConnectedNotifier
fmt.Println("Receiver: 准备接收数据")
fmt.Println("Receiver: 等待退出")
<-QuitNotifier
fmt.Println("Receiver: 退出")
ReceiverQuitedNotifier <- 1
}
func Sender(){
<- ConnectorConnectedNotifier
fmt.Println("Sender: 准备发送数据")
fmt.Println("Sender: 等待退出")
<-QuitNotifier
fmt.Println("Sender: 退出")
SenderQuitedNotifier <- 1
}
func HeartBeat(){
<- ConnectorConnectedNotifier
fmt.Println("HeartBeat: 准备处理心跳")
fmt.Println("HeartBeat: 等待退出")
<-QuitNotifier
fmt.Println("HeartBeat: 退出")
HeartBeatQuitedNotifier <- 1
}
代码输出
等待退出
准备连接
连接成功
HeartBeat: 准备处理心跳
HeartBeat: 等待退出
Sender: 准备发送数据
Receiver: 准备接收数据
Receiver: 等待退出
Sender: 等待退出
- Ctrl+C pressed in Terminal
准备退出,开始清理
Sender: 退出
HeartBeat: 退出
Connector退出
Receiver: 退出
主程序退出
言简意赅,赞一个!