zoukankan      html  css  js  c++  java
  • Go语言系列- Goroute和Channel

    一、Goroute

    1. 进程和线程

    •   A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配合调度的一个独立单位

    •   B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。

    •   C. 一个进程可以创建和撤销多个线程:同一个进程中的多个线程之间可以并发执行。

    2. 并发和并行

    •   A. 多线程程序在一个核的cpu上运行,就是并发

    •   B. 多线程程序在多个核的cpu上运行,就是并行

    3. 协程和线程

    • 协程:独立的栈空间,共享栈空间,调度由用户自己控制,本质上有点类似于用户级线程,这些用户级线程的调度也是自己实现的

    • 线程:一个线程上可以拍多个协程, 协程是轻量级的线程

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func test() {
        var i int
        for {
            fmt.Println(i)
            time.Sleep(time.Second)
            i++
        }
    }
    
    func main() {
        go test()
        time.Sleep(time.Second * 10)
        // for {
        //     fmt.Println("i' running in main")
        //     time.Sleep(time.Second)
        // }
    }
    goroutine

     4. goroutine调度模型

     

     5. 如何设置golang运行的cpu核数

    package main
    
    import (
        "fmt"
        "sync"
        "time"
    )
    
    type task struct {
        n int
    }
    
    var (
        m    = make(map[int]uint64)
        lock sync.Mutex // 互斥锁
    )
    
    func calc(t *task) {
        var sum uint64
        sum = 1
        for i := uint64(1); i < uint64(t.n); i++ {
            sum *= i
        }
        fmt.Println(t.n, sum)
        lock.Lock()
        m[t.n] = sum
        lock.Unlock()
    }
    
    func main() {
        for i := 0; i < 16; i++ {
            t := &task{n: i}
            go calc(t)
        }
        time.Sleep(time.Second * 10)
        lock.Lock()
        for k, v := range m {
            fmt.Printf("%d! = %v
    ", k, v)
        }
        lock.Unlock()
    }
    goroute_lock

    6. 协程管理

    golang 同步等待所有协程执行完毕sync WaitGroup

    golang的sync的包有一个功能WaitGroup

    作用:阻塞主线程的执行,直到所有的goroutine执行完成,说白了就是必须同步等待所有异步操作完成!!!

    func (wg *WaitGroup) Add(delta int):等待协程的数量。
    
    func (wg *WaitGroup) Done(): 减少waitgroup线程等待线程数量的值,一般在协程完成之后执行。
    
    func (wg *WaitGroup) Wait():wait方法一般在主线程调用,阻塞直到group计数减少为0。

    示例

    package main
     
    import (
        "fmt"
        "sync"
        "time"
    )
     
    func main() {
        var wg sync.WaitGroup
     
        for i := 0; i < 5; i = i + 1 {
            wg.Add(1)
            go func(n int) {
                // defer wg.Done()
                defer wg.Add(-1)
                EchoNumber(n)
            }(i)
        }
     
        wg.Wait()
    }
     
    func EchoNumber(i int) {
        time.Sleep(3e9)
        fmt.Println(i)
    }
    

    程序很简单,只是将每次循环的数量过3秒钟输出。那么,这个程序如果不用WaitGroup,那么将看不见输出结果。因为goroutine还没执行完,主线程已经执行完毕。注释的defer wg.Done()和defer wg.Add(-1)作用一样。

    二、Channel

    1.不同的goroutine之间如何进行通讯?

      a. 全局变量和锁同步

      b. Channel

    2. channel概念

      a. 类似unix中管道(pipe)

      b. 先进先出

      c. 线程安全,多个goroutine同时访问,不需要de加锁

      d. channel是有类型的,一个整数的channel只能存放整数

     3. channel声明

    var 变量名 chan 类型
    var test chan int
    var test chan string
    var test chan map[string]string
    var test chan stu
    var test chan *stu
    

    4. channel初始化

    使用make进行初始化,比如:

    var test chan int
    
    test = make(chan int, 10)
    
    
    
    var test chan string
    
    test = make(chan string, 10)
    

    5. channel基本操作

    1. 从channel读取数据
    var testChan chan int
    testChan = make(chan int, 10)
    var a int
    a = <- testChan
    2. 从channel写入数据
    var testChan chan int
    testChan = make(chan int, 10)
    var a int = 10
    testChan <- a
    
    package main
    
    import (
        "fmt"
        "time"
    )
    
    func write(ch chan int) {
        for i := 0; i < 100; i++ {
            ch <- i
            fmt.Println("put data: ", i)
        }
    }
    
    func read(ch chan int) {
        for {
            var b int
            b = <-ch
            fmt.Println(b)
            time.Sleep(time.Second)
        }
    }
    
    func main() {
        intChan := make(chan int, 10)
        go write(intChan)
        go read(intChan)
    
        time.Sleep(time.Second * 10)
    }
    goroutine_chan

    6.channel阻塞

    7. 带缓冲区的channel

      1. 如下所示,testChan 只能放一个元素

    testChan := make(chan int)
    var a int
    a = <- testChan    
    

      2. 如下所示,testChan是带缓冲区的chan,一次可以放10个元素

    testChan = make(chan int, 10)
    var a int
    testChan <- a
    
    package main
    
    import (
        "fmt"
    )
    
    func send(ch chan<- int, exitChan chan struct{}) {
        for i := 0; i < 10; i++ {
            ch <- i
        }
        close(ch)
        var a struct{}
        exitChan <- a
    }
    
    func recv(ch <-chan int, exitChan chan struct{}) {
        for {
            v, ok := <-ch
            if !ok {
                break
            }
            fmt.Println(v)
        }
        var a struct{}
        exitChan <- a
    }
    
    func main() {
        var ch chan int
        ch = make(chan int, 10)
        exitChan := make(chan struct{}, 2)
    
        go send(ch, exitChan)
        go recv(ch, exitChan)
        var total = 0
        for _ = range exitChan {
            total++
            if total == 2 {
                break
            }
        }
    }
    channel_readline
    package main
    
    import "fmt"
    
    type student struct {
        name string
    }
    
    func main() {
        var intChan chan int
        intChan = make(chan int, 10)
        intChan <- 10
    
        var stringChan chan map[string]string
        stringChan = make(chan map[string]string, 10)
        m := make(map[string]string, 16)
        m["stu01"] = "001"
        m["stu01"] = "002"
        stringChan <- m
    
        var stuChan chan *student
        stuChan = make(chan *student, 10)
        stu := student{name: "stud01"}
        stuChan <- &stu
    
        var stuInterChan chan interface{}
        stuInterChan = make(chan interface{}, 10)
        stu1 := student{name: "stu01"}
        stuInterChan <- &stu1
    
        var stu01 interface{}
        stu01 = <-stuInterChan
    
        fmt.Println(stu01)
    
        var stu02 *student
        stu02, ok := stu01.(*student)
        if !ok {
            fmt.Println("can not convert")
            return
        }
        fmt.Println(stu02)
    }
    channel

    8. chan之间的同步 

    package main
    
    import (
        "fmt"
    )
    
    func calc(taskChan chan int, resChan chan int, exitChan chan bool) {
        for v := range taskChan {
            flag := true
            for i := 2; i < v; i++ {
                if v%i == 0 {
                    flag = false
                    break
                }
            }
            if flag {
                resChan <- v
            }
        }
        fmt.Println("exit")
        exitChan <- true
    }
    
    func main() {
        intChan := make(chan int, 1000)
        resultChan := make(chan int, 1000)
        exitChan := make(chan bool, 8)
    
        go func() {
            for i := 0; i < 100000; i++ {
                intChan <- i
            }
            close(intChan)
        }()
        for i := 0; i < 8; i++ {
            go calc(intChan, resultChan, exitChan)
        }
        // 等待所有的groutine全部退出
        go func() {
            for i := 0; i < 8; i++ {
                <-exitChan
                fmt.Println("wait goroute", i, "exited")
            }
            close(resultChan)
        }()
        for v := range resultChan {
            fmt.Println(v)
        }
    }
    goroutine_sync
    package main
    
    import (
        "fmt"
    )
    
    func send(ch chan int, exitChan chan struct{}) {
        for i := 0; i < 10; i++ {
            ch <- i
        }
        close(ch)
        var a struct{}
        exitChan <- a
    }
    
    func recv(ch chan int, exitChan chan struct{}) {
        for {
            v, ok := <-ch
            if !ok {
                break
            }
            fmt.Println(v)
        }
        var a struct{}
        exitChan <- a
    }
    
    func main() {
        ch := make(chan int, 10)
        exitChan := make(chan struct{}, 2)
        go send(ch, exitChan)
        go recv(ch, exitChan)
        var total = 0
        for _ = range exitChan {
            total++
            if total == 2 {
                break
            }
        }
    }
    goroutine_sync2

    9.for range遍历chan

    package main
    
    import "fmt"
    
    func main() {
    	var ch chan int
    	ch = make(chan int, 1000)
    
    	for i := 0; i < 1000; i++ {
    		ch <- i
    	}
    
    	close(ch)
    	for v := range ch {
    		fmt.Println(v)
    	}
    }

    10. chan的关闭

      1. 使用内置函数close进行关闭,chan关闭之后,for range遍历chan中已经放入的元素

      2. 使用内置函数close进行关闭, chan关闭之后,没有使用for range的写法,需要判断chan是否关闭。

    示例

    package main
    
    import "fmt"
    
    func main() {
    	var ch chan int
    	ch = make(chan int, 10)
    
    	for i := 0; i < 10; i++ {
    		ch <- i
    	}
    	close(ch)
    	for {
    		var b int
    		b, ok := <-ch
    		if ok == false {
    			fmt.Println("chan is close")
    			break
    		}
    		fmt.Println(b)
    	}
    }

    11. chan的只读和只写

      a. 只读chan的声明

    var 变量的名字 <-chan int
    var readChan <-chan int

      b.只写chan的声明

    var 变量的名字 chan <- int
    var writeChan chan <- int
    
    package main
    
    import (
        "bufio"
        "fmt"
        "io"
        "os"
    )
    
    func main() {
        file, err := os.Open("test.log")
        if err != nil {
            fmt.Println(err)
            return
        }
        defer file.Close()
        // 带缓存区的文件读写
        reader := bufio.NewReader(file)
        var line []byte
        for {
            data, prefix, err := reader.ReadLine()
            if err == io.EOF {
                break
            }
            line = append(line, data...)
            if !prefix {
                fmt.Printf("data: %s
    ", string(data))
                line = line[:]
            }
        }
        // fmt.Println(line)
    }
    带缓存区的读写

    12.对chan进行select操作

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func main() {
    	var ch chan int
    	ch = make(chan int, 10)
    	ch2 := make(chan int, 10)
    	go func() {
    		var i int
    		for {
    			ch <- i
    			time.Sleep(time.Second)
    			ch2 <- i * i
    			time.Sleep(time.Second)
    			i++
    		}
    	}()
    
    	for {
    		select {
    		case v := <-ch:
    			fmt.Println(v)
    		case v := <-ch2:
    			fmt.Println(v)
    		case <-time.After(time.Second):
    			fmt.Println("get data timeout")
    			time.Sleep(time.Second)
    		}
    		// var b int
    		// b = <-ch
    		// fmt.Println(b)
    	}
    }

    13.定时器的使用

    package main
    
    import (
    	"runtime"
    	"time"
    )
    
    func main() {
    	num := runtime.NumCPU()
    	runtime.GOMAXPROCS(num - 1)
    	for i := 0; i < 1024; i++ {
    		go func() {
    			for {
    				select {
    				case <-time.After(time.Microsecond):
    					// fmt.Println("get data timeout")
    				}
    			}
    		}()
    	}
    	time.Sleep(time.Second * 1000)
    }

    14.一次定时器、超时控制

    package main
    
    import (
    	"fmt"
    	"runtime"
    	"time"
    )
    
    func main() {
    	num := runtime.NumCPU()
    	runtime.GOMAXPROCS(num - 1)
    	for i := 0; i < 16; i++ {
    		go func() {
    			for {
    				t := time.NewTicker(time.Second)
    				select {
    				case <-time.After(time.Microsecond):
    					fmt.Println("timeout")
    				}
    				t.Stop()
    			}
    		}()
    	}
    	time.Sleep(time.Second * 1000)
    }

    15.goroutine中使用recover

    package main
    
    import (
        "fmt"
        "runtime"
        "time"
    )
    
    func test() {
        defer func() {
            if err := recover(); err != nil {
                fmt.Println("panic:", err)
            }
        }()
    
        var m map[string]int
        m["stu"] = 100
    }
    
    func calc() {
        for {
            fmt.Println("i'm calc")
            time.Sleep(time.Second)
        }
    }
    
    func main() {
        num := runtime.NumCPU()
        runtime.GOMAXPROCS(num - 1)
        go test()
        for i := 0; i < 100; i++ {
            go calc()
        }
    
        time.Sleep(time.Second * 1000)
    }

    实战

    package response
    
    import "sync"
    
    type ChannelController struct {
        Channel chan bool
        WaitGroup *sync.WaitGroup
    }
    response.go
    func UpdateCasbin2(casbinRecive request.CasbinInReceive) error {
        e := Casbin()
        // 清空指定用户所有权限,若更新权限为空 则返回
        ClearCasbin(e, 0, casbinRecive.RoleId)
        if len(casbinRecive.CasbinInfos) == 0 {
            return nil
        }
        // 更新用户权限
        var ChanController response.ChannelController
        ChanController.Channel = make(chan bool, len(casbinRecive.CasbinInfos))
        ChanController.WaitGroup = &sync.WaitGroup{}
        ChanController.WaitGroup.Add(len(casbinRecive.CasbinInfos))
        for _, v := range casbinRecive.CasbinInfos {
            go AddCasbin(e, v, casbinRecive.RoleId, ChanController)
        }
        ChanController.WaitGroup.Wait()
        close(ChanController.Channel)
    
        for addFlag := range ChanController.Channel{
            if !addFlag {
                return errors.New("存在相同api,添加失败,请联系管理员")
            }
        }
        return nil
    }
    
    func AddCasbin(e *casbin.Enforcer, v request.CasbinInfo, roleId string, chan_contro response.ChannelController) {
        defer chan_contro.WaitGroup.Done()
        cm := model.CasbinRule{
            Ptype:       "p",
            RoleId: roleId,
            Path:        v.Path,
            Method:      v.Method,
        }
        chan_contro.Channel <- e.AddPolicy(cm.RoleId, cm.Path, cm.Method)
    }
    service/sys_casbin.go

    三、单元测试

    • 1.文件名必须以_test.go结尾

    • 2. 使用Test开头的函数名作为测试函数

    • 3. 测试案例

    package main
    
    func add(a, b int) int {
        return a + b
    }
    
    func sub(a, b int) int {
        return a - b
    }
    calc.go
    package main
    
    import (
        "encoding/json"
        "io/ioutil"
    )
    
    type student struct {
        Name string
        Sex  string
        Age  int
    }
    
    func (p *student) Save() (err error) {
        data, err := json.Marshal(p)
        if err != nil {
            return
        }
        err = ioutil.WriteFile("stu.dat", data, 0755)
        return
    }
    
    func (p *student) Load() (err error) {
        data, err := ioutil.ReadFile("stu.dat")
        if err != nil {
            return
        }
        err = json.Unmarshal(data, p)
        return
    }
    student.go
    package main
    
    import "testing"
    
    func TestAdd(t *testing.T) {
        r := add(2, 4)
        if r != 6 {
            t.Fatalf("add(2,4) error, expect:%d, actual:%d", 6, r)
        }
        t.Log("test add succ")
    }
    func TestSub(t *testing.T) {
        r := sub(2, 4)
        if r != -2 {
            t.Fatalf("sub(2,4) error, expect:%d, actual:%d", -2, r)
        }
        t.Logf("test sub succ")
    }
    calc_test.go
    package main
    
    import "testing"
    
    func TestSave(t *testing.T) {
        stu := &student{
            Name: "stu01",
            Sex:  "man",
            Age:  10,
        }
        err := stu.Save()
        if err != nil {
            t.Fatalf("save student failed: err%v", err)
        }
    }
    
    func TestLoad(t *testing.T) {
        stu := &student{
            Name: "stu01",
            Sex:  "man",
            Age:  10,
        }
    
        err := stu.Save()
        if err != nil {
            t.Fatalf("save student failed: err%v", err)
        }
        stu2 := &student{}
        err = stu2.Load()
        if err != nil {
            t.Fatalf("load student failed,err: %v", err)
        }
    
        if stu.Name != stu2.Name {
            t.Fatalf("load student failed, Name not equal")
        }
    
        if stu.Age != stu2.Age {
            t.Fatalf("load student failed, Age not equal")
        }
    
        if stu.Sex != stu2.Sex {
            t.Fatalf("load student failed, Sex not equal")
        }
    }
    student_test.go
    package main
    main.go
  • 相关阅读:
    条件语句、循环语句
    var、符号运算、条件语句、三元(目)运算、自加和自减
    js的介绍
    浏览器的差距、ie6 ie7 ie8、符号、html css、BFC、
    单位、浏览器、布局、
    z-index、absolute、marquee滚动条的问题
    js数据类型 方法 函数
    js函数
    全局方法或全局属性
    数据类型
  • 原文地址:https://www.cnblogs.com/zhangyafei/p/10732795.html
Copyright © 2011-2022 走看看