语言基础
字符串 数组 切片
数组的定义
var a [3]int // 定义长度为3的int型数组, 元素全部为0var b = [...]int{1, 2, 3} // 定义长度为3的int型数组, 元素为 1, 2, 3var c = [...]int{2: 3, 1: 2} // 定义长度为3的int型数组, 元素为 0, 2, 3var 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() errorValue(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())returndefault: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())returndefault: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())returndefault: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())returndefault: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())returndefault: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())returndefault:fmt.Println("子context的协程监控中,time=", time.Now().Unix())fmt.Println("子获取值", ctx.Value("keys"))time.Sleep(1 * time.Second)}}}
锁的使用
互斥锁
错误示范
使用两个携程进行对一个变量进行操作,理论上讲最终a应该时0
var (a = 0wg 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 = 0wg sync.WaitGroupmutex 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 += imutex.Unlock()}}func Sub() {defer wg.Done()for i := 0; i < 100000; i++ {mutex.Lock()a -= imutex.Unlock()}}
控制逻辑处理器数量
var (a = 0wg 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)returndefault:fmt.Print("持续等待中...")}}}()time.Sleep(time.Second * 2)chans <- atime.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 NoticeTypejson.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.WaitGroupwg.Add(2)chanData := make(chan []byte, 10000)go redis.MsgPop(chanData)go redis.MsgPush(chanData)wg.Wait()}
