语言基础
字符串 数组 切片
数组的定义
var a [3]int // 定义长度为3的int型数组, 元素全部为0
var b = [...]int{1, 2, 3} // 定义长度为3的int型数组, 元素为 1, 2, 3
var c = [...]int{2: 3, 1: 2} // 定义长度为3的int型数组, 元素为 0, 2, 3
var d = [...]int{1, 2, 4: 5, 6} // 定义长度为6的int型数组, 元素为 1, 2, 0, 0, 5, 6
字符串 ```go //for range对字符串的迭代模拟实现 func forOnString(s string) { for i := 0; len(s) > 0; {
r, size := utf8.DecodeRuneInString(s)
fmt.Println(string(r),size,s)
s = s[size:]
i += size
} }
<a name="vh45M"></a>
# Context
```go
/*
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
Deadline会返回一个超时时间,Goroutine获得了超时时间后,例如可以对某些io操作设定超时时间。
Done方法返回一个信道(channel),当Context被撤销或过期时,该信道是关闭的,即它是一个表示Context是否已关闭的信号。
当Done信道关闭后,Err方法表明Context被撤的原因。
Value可以让Goroutine共享一些数据,当然获得数据是协程安全的。但使用这些数据的时候要注意同步,比如返回了一个map,而这个map的读写则要加锁
*/
WithCancel
WithCancel创建的上下文需要主动调用cancel函数来结束上下文的生命周期
func main() {
// 父context(利用根context得到)
ctx, cancel := context.WithCancel(context.Background())
// 父context的子协程
go watch1(ctx)
// 子context,注意:这里虽然也返回了cancel的函数对象,但是未使用
valueCtx, _ := context.WithCancel(ctx)
// 子context的子协程
go watch2(valueCtx)
fmt.Println("现在开始等待3秒,time=", time.Now().Unix())
time.Sleep(3 * time.Second)
// 调用cancel()
fmt.Println("等待3秒结束,调用cancel()函数")
// 必须主动调用
cancel()
// 再等待5秒看输出,可以发现父context的子协程和子context的子协程都会被结束掉
time.Sleep(5 * time.Second)
fmt.Println("最终结束,time=", time.Now().Unix())
}
// 父context的协程
func watch1(ctx context.Context) {
for {
select {
case <-ctx.Done(): //取出值即说明是结束信号
fmt.Println("收到信号,父context的协程退出,time=", time.Now().Unix())
return
default:
fmt.Println("父context的协程监控中,time=", time.Now().Unix())
time.Sleep(1 * time.Second)
}
}
}
// 子context的协程
func watch2(ctx context.Context) {
for {
select {
case <-ctx.Done(): //取出值即说明是结束信号
fmt.Println("收到信号,子context的协程退出,time=", time.Now().Unix())
return
default:
fmt.Println("子context的协程监控中,time=", time.Now().Unix())
time.Sleep(1 * time.Second)
}
}
}
WithDeadline
WithDeadline 创建的上下文过期时间是指的某一个时刻,参数类型time.TIme可以看出,当父context到达过期时间时,所有由该父context创建的上下文全部过期
func main() {
// 父context(利用根context得到)
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second))
defer cancel()
// 父context的子协程
go watch1(ctx)
// 子context,注意:这里虽然也返回了cancel的函数对象,但是未使用
valueCtx, _ := context.WithCancel(ctx)
// 子context的子协程
go watch2(valueCtx)
// 再等待5秒看输出,可以发现父context的子协程和子context的子协程都会被结束掉
time.Sleep(10 * time.Second)
fmt.Println("最终结束,time=", time.Now().Unix())
}
// 父context的协程
func watch1(ctx context.Context) {
for {
select {
case <-ctx.Done(): //取出值即说明是结束信号
fmt.Println("收到信号,父context的协程退出,time=", time.Now().Unix())
fmt.Println(ctx.Err().Error())
return
default:
fmt.Println("父context的协程监控中,time=", time.Now().Unix())
time.Sleep(1 * time.Second)
}
}
}
// 子context的协程
func watch2(ctx context.Context) {
for {
select {
case <-ctx.Done(): //取出值即说明是结束信号
fmt.Println("收到信号,子context的协程退出,time=", time.Now().Unix())
return
default:
fmt.Println("子context的协程监控中,time=", time.Now().Unix())
time.Sleep(1 * time.Second)
}
}
}
WithTimeout
WithTimeout 创建的上下文传入的时间参数可以理解为现在开始Context剩余的生命时长即指定总共的生命周期时长
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
WithValue
用来和父context共享一些数据
func main() {
// 父context(利用根context得到)
ctx := context.WithValue(context.Background(), "keys", "valus")
// 父context的子协程
go watch1(ctx)
// 子context,注意:这里虽然也返回了cancel的函数对象,但是未使用
valueCtx, _ := context.WithCancel(ctx)
//// 子context的子协程
go watch2(valueCtx)
// 再等待5秒看输出,可以发现父context的子协程和子context的子协程都会被结束掉
time.Sleep(10 * time.Second)
fmt.Println("最终结束,time=", time.Now().Unix())
}
// 父context的协程
func watch1(ctx context.Context) {
for {
select {
case <-ctx.Done(): //取出值即说明是结束信号
fmt.Println("收到信号,父context的协程退出,time=", time.Now().Unix())
fmt.Println(ctx.Err().Error())
return
default:
fmt.Println("父context的协程监控中,time=", time.Now().Unix())
fmt.Println("获取值", ctx.Value("keys"))
time.Sleep(1 * time.Second)
}
}
}
// 子context的协程
func watch2(ctx context.Context) {
for {
select {
case <-ctx.Done(): //取出值即说明是结束信号
fmt.Println("收到信号,子context的协程退出,time=", time.Now().Unix())
return
default:
fmt.Println("子context的协程监控中,time=", time.Now().Unix())
fmt.Println("子获取值", ctx.Value("keys"))
time.Sleep(1 * time.Second)
}
}
}
锁的使用
互斥锁
错误示范
使用两个携程进行对一个变量进行操作,理论上讲最终a应该时0
var (
a = 0
wg sync.WaitGroup
)
func main() {
wg.Add(2)
go Add()
go Sub()
wg.Wait()
fmt.Println(a) // 答案不唯一
}
func Add() {
defer wg.Done()
for i := 0; i < 100000; i++ {
a += i
}
}
func Sub() {
defer wg.Done()
for i := 0; i < 100000; i++ {
a -= i
}
}
使用互斥锁实现
var (
a = 0
wg sync.WaitGroup
mutex sync.Mutex
)
func main() {
wg.Add(2)
go Add()
go Sub()
wg.Wait()
fmt.Println(a)
}
func Add() {
defer wg.Done()
for i := 0; i < 100000; i++ {
mutex.Lock()
a += i
mutex.Unlock()
}
}
func Sub() {
defer wg.Done()
for i := 0; i < 100000; i++ {
mutex.Lock()
a -= i
mutex.Unlock()
}
}
控制逻辑处理器数量
var (
a = 0
wg sync.WaitGroup
)
func main() {
// 分配一个逻辑处理器给调度器使用
runtime.GOMAXPROCS(1)
wg.Add(2)
go Add()
go Sub()
wg.Wait()
fmt.Println(a)
}
func Add() {
defer wg.Done()
for i := 0; i < 100000; i++ {
a += i
}
}
func Sub() {
defer wg.Done()
for i := 0; i < 100000; i++ {
a -= i
}
}
Channel 通道
数据获取
func main() {
// 创建无缓冲的通道
chans := make(chan int, 10)
// 方案一 阻塞获取数据
//go func() {
// // 阻塞获取
// data := <-chans
// fmt.Println("获取到数据", data)
//}()
// 方案二 select + for + default 轮询获取
//go func() {
// for {
// select {
// case data := <-chans:
// fmt.Println("获取到数据", data)
// return
// default:
// fmt.Print("等待中")
// }
// }
//}()
// 方案三 select 无 default
// 如果使用 select 加default 则无法获取到数据 需要添加无限循环来处理
//go func() {
// select {
// case data := <-chans:
// fmt.Println("获取到数据", data)
// return
// }
//}()
// 方案四 select + default + for
// 由于 select 存在default分支 故select会结束 但是存在For 所以
// 可以继续执行获取操作
go func() {
for {
select {
case data := <-chans:
fmt.Println("获取到数据", data)
return
default:
fmt.Print("持续等待中...")
}
}
}()
time.Sleep(time.Second * 2)
chans <- a
time.Sleep(time.Second * 1)
}
发送接收案例
// MsgPop 消息获取
func MsgPop(msg chan []byte) {
for v := range msg {
// fmt.Println(v, "获取数据")
SendMsg(v)
fmt.Println("等待中")
}
}
// 消息发送
func MsgPush(cc chan []byte) {
a := map[int]string{
0: "wx-notice1",
1: "wm-notice2",
2: "wm-notice3",
3: "wm-notice4",
4: "wm-notice5",
5: "wm-notice6",
6: "wm-notice7",
7: "wm-notice8",
8: "wm-notice9",
9: "wm-notice10",
}
for i := 0; i < 10; i++ {
time.Sleep(time.Second)
msg := NoticeType{
MsgType: a[i],
MsgInfo: "zhangsan",
}
data, _ := json.Marshal(msg)
cc <- data
}
}
// 业务实现
func SendMsg(msg []byte) {
var not NoticeType
json.Unmarshal(msg, ¬)
fmt.Println("获取到数据", not)
switch not.MsgType {
case "wx-notice1":
fmt.Println("wx-notice1")
case "wm-notice2":
fmt.Println("wm-notice2")
default:
fmt.Println("其他")
}
func main() {
var wg sync.WaitGroup
wg.Add(2)
chanData := make(chan []byte, 10000)
go redis.MsgPop(chanData)
go redis.MsgPush(chanData)
wg.Wait()
}