goland 学习 之channel

  首先要明确channel是什么

       channel是一种类型,一种引用类型。声明通道类型的格式如下:   

        var test chan type

        test := make(chan struct{})  (make只能创建 slice,map,chan) 

 make和new的区别  引用资料  (make 创建一个类型,并初始化,返回引用   new 创建一个类型 并初始化零值,返回内存地址)

 指针和引用的区别   引用资料  (指针是引用的一种特例,是一类简单的透明引用,区别于不透明的引用)

  接下来要知道channel的作用

        goland程序并发执行体是goroutine(G协程)。如果同时有多个goroutine并发执行,然后这时不同goroutine又需要做数据交互时怎么办呢?常规做法是利用共享内存来处理,但是在并发的情况下共享内存的使用会有竟态问题,就需要加解锁操作,这就会造成资源消耗。 所以Go语言的并发模型是CSP(Communicating Sequential Processes),提倡通过通信共享内存而不是通过共享内存而实现通信。这样channel就起到了通信管道的作用

     channel的分类

          有缓冲channel

               可以理解为自带一个存储池的管道 ,池满则发送方阻塞,池空就接收方阻塞   (可以利用这个特性做限流降级等功能,常见如令牌桶,漏桶等功能)

          无缓冲

              就是一根管子,相当于缓存空间为1单位的有缓冲管道。

 

应用一  循环打印 1234

    

package main

import (
    "fmt"
    "time"
)

type token struct{}

func channel2() {
    num := 4
    var chs []chan token
    // 4 个work
    for i := 0; i < num; i++ {
        chs = append(chs, make(chan token))
    }
    for j := 0; j < num; j++ {
        go worker(j, chs[j], chs[(j+1)%num])
    }
    // 先把令牌交给第一个
    chs[0] <- struct{}{}
    select {}
}

func worker(id int, ch chan token, next chan token) {
    for {
        // 对应work 取得令牌
        token := <-ch
        fmt.Println(id + 1)
        time.Sleep(1 * time.Second)
        // 传递给下一个
        next <- token
    }
}

func main() {
    channel2()
}

 

 

 

应用二 有缓冲的channel实现限流

 

package main

import (
    "fmt"
    "time"
)

func channel3() {
    limit := make(chan struct{}, 3)
    jobCount := 10
    start(jobCount, limit)
    select {}
    time.Sleep(time.Duration(50) * time.Second)
}

func start(jobCount int, limit chan struct{}) {
    for i := 0; i < jobCount; i++ {
        go func(index int) {
            limit <- struct{}{}
            job(index)
            <-limit
        }(i)
    }
}

func job(index int) {
    // 耗时任务
    time.Sleep(time.Duration(index) * time.Second)
    fmt.Println("任务:已完成:", index, time.Now().Format("2006-01-02 15:04:05"), "\n")
}

 

应用三 带超时的限流器

 

package main

import "goland/utill"

func channel4() {
    s := utill.CreateLimitServer(300, 3)
    for i := 0; i < 10; i++ {
        go s.LimitStart(i)
    }
    select {}
}





package utill

import (
    "fmt"
    "time"
)

type Server struct {
    tk     *time.Ticker
    reset  chan struct{}
    Close  chan struct{}
    Limit  chan struct{}
    Period int64
}

func CreateServer(Period int64) *Server {
    return &Server{
        tk:     nil,
        reset:  make(chan struct{}),
        Limit:  make(chan struct{}),
        Close:  make(chan struct{}),
        Period: Period,
    }
}

func CreateLimitServer(Period int64, limit int) *Server {
    return &Server{
        tk:     nil,
        reset:  make(chan struct{}),
        Limit:  make(chan struct{}, limit),
        Close:  make(chan struct{}),
        Period: Period,
    }
}

func (s *Server) Update(p int64) {
    s.Period = p
    s.reset <- struct{}{}
}

func (s *Server) Stop() {
    //s.Close <- Type
    close(s.Close)
    close(s.reset)
}

// 程序启动
func (s *Server) Start() {
    // 定时
    s.tk = time.NewTicker(time.Duration(s.Period) * time.Second)
    defer s.tk.Stop()
    for {
        select {
        case <-s.tk.C:
            fmt.Println("定时唤醒:", time.Now().Format("2006-01-02 15:04:05"))
        case <-s.reset:
            fmt.Println("reset:", s.Period, time.Now().Format("2006-01-02 15:04:05"))
            s.tk.Reset(time.Duration(s.Period) * time.Second)
        case <-s.Close:
            fmt.Println("Close:")
            return

        }
    }
}

func (s *Server) LimitStart(index int) {
    // 定时
    s.Limit <- struct{}{}
    jobs(s, index)
    s.tk = time.NewTicker(time.Duration(s.Period) * time.Second)
    defer s.tk.Stop()
    for {
        select {
        case <-s.tk.C:
            fmt.Println("定时唤醒:", time.Now().Format("2006-01-02 15:04:05"))
        case <-s.reset:
            fmt.Println("reset:", s.Period, time.Now().Format("2006-01-02 15:04:05"))
            s.tk.Reset(time.Duration(s.Period) * time.Second)
        case <-s.Close:
            fmt.Println("Close:")
            return

        }
    }
}

func jobs(s *Server, index int) {
    // 耗时任务
    time.Sleep(2 * time.Second)
    fmt.Println("任务:已完成:", index, time.Now().Format("2006-01-02 15:04:05"), "\n")
    <-s.Limit
}

 

posted @ 2023-02-27 17:11  给香菜送点香菜  阅读(78)  评论(0)    收藏  举报