zoukankan      html  css  js  c++  java
  • 并发Goroute、定时器、信号处理、单元测试

    Goroutine(轻量级的线程,开线程没有数量限制)

    1.进程和线程
      A.进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。
      B.线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
      C.一个进程可以创建和撤销多个线程;同一个进程中的多个线程之间可以并发执行。

    2、进程和线程

      ngix是多进程的单线程程序

     3.并发和并行
       A.多线程程序在一个核的cpu上运行,就是并发。go多线程的切换都是在用户态操作的,不像其他语言先切换到内核态,完成线程切换,然后返回用户态继续执行程序。
       B.多线程程序在多个核的cpu上运行,就是并行

    4.协程和线程
      协程:独立的栈空间,共享堆空间,调度由用户自己控制,本质上有点类似于用户级线程,这些用户级线程的调度也是自己实现的
      线程:一个线程上可以跑多个协程,协程是轻量量级的线程。一个线程可以跑多个Goroutine。

    5.goroutine调度模型

     M是物理线程,P是资源,G是Goroutine

    如果有IO操作时,会新起一个线程等待IO操作的Goroutine

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

      1.5之前go需要手动设置程序执行的内核数,1.5之后go自动设置

    package main
    
    import (
        "fmt"
        "runtime"
    )
    
    func main() {
        num := runtime.NumCPU()  //查看有几个内核
        fmt.Printf("cpu num:%d
    ", num) 
        runtime.GOMAXPROCS(1)    //设置有程序用几个内核执行
    }

    7、不同goroutine之间进行通讯  

      a.全局变量

    package main
    
    import (
        "fmt"
        "time"
    )
    
    var exits [3]bool
    
    func calc(index int) {
        for i := 0; i < 1000; i++ {
            time.Sleep(time.Millisecond) //休息1毫秒
        }
        exits[index] = true
    }
    
    func main() {
        start := time.Now().UnixNano()
        go calc(0) //起一个goroutine,不用go就是串行
        go calc(1)
        go calc(2)
    
        for { //保证主线程不挂断,子线程才能执行
            if exits[0] && exits[1] && exits[2] {
                break
            }
            time.Sleep(time.Millisecond)
        }
        end := time.Now().UnixNano()
        fmt.Printf("finished, cost:%d ms
    ", (end-start)/1000/1000)
    }
    View Code

      b.锁同步

    package main
    
    import (
        "fmt"
        "sync"
        "time"
    )
    
    func calc(index int, waitGroup *sync.WaitGroup) {
        for i := 0; i < 1000; i++ {
            time.Sleep(time.Millisecond)
        }
    
        waitGroup.Done() //释放一把锁
    }
    
    func main() {
        var waitGroup sync.WaitGroup //等待一组Goroutine执行完毕
        start := time.Now().UnixNano()
        for i := 0; i < 3; i++ {
            waitGroup.Add(1) //添加一把锁
            go calc(i, &waitGroup)
        }
    
        waitGroup.Wait()
        end := time.Now().UnixNano()
        fmt.Printf("finished, cost:%d ms
    ", (end-start)/1000/1000)
    }
    View Code

      c.Channel(oroutine和channel相结合)

    package main
    
    import (
        "fmt"
        "time"
    )
    
    
    func () {
        var intChan chan int = make(chan int, 1) //如果是0就是没有缓冲区的管道,必须有程序来取才能放进去
        go func() {
            intChan <- 10
        }()
    
        result := <-intChan
        fmt.Printf("result:%d
    ", result)
    }
    View Code

     8.goroutine中使用recover

    应用场景,如果某个goroutine panic了,而且这个goroutine 里面没有捕获(recover),那么整个进程就会挂掉。所以,好的习惯是每当go产生一个goroutine,就需要写下recover。

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func calc() {
    
        defer func() { //defer必须放置在最前面,才能捕获后面所有的panic,程序退出时执行defer
            err := recover() //捕获goroutine错误
            if err != nil {
                fmt.Println(err)
            }
        }()
    
        var p *int
        *p = 100
    }
    
    func main() {
        go calc()
        time.Sleep(time.Second * 3)
        fmt.Println("progress exited")
    }
    View Code

    Channel

    1、channel概念 

    • 类似unix中管道(pipe) 
    • 先进先出
    • 线程安全,多个goroutine同时访问,不需要加锁
    • channel是有类型的, 一个整数的channel只能存放整数

    2、 channel声明 

      var 变量量名 chan 类型 

      var test chan int 

      var test chan string 

      var test chan map[string]string 

      var test chan stu

    3、channel初始化

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

      var test chan int

      test = make(chan int, 10) 

      var test chan string

      test = make(chan string, 10)

    4、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

    5.带缓冲区的channel
       1.如下所示,testChan只能放 一个元素:
          var testChan chan int
          testChan = make(chan int)
          var a int
          a = <- testChan
       2.如下所示,testChan是带缓冲区的chan, 一次可以放10个元素:
          var testChan chan int
          testChan = make(chan int, 10)
          var a int = 10
          testChan <- a

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func test() {
        var intChan chan int = make(chan int, 1) //如果是0就是没有缓冲区的管道,必须有程序来取才能放进去
        go func() {
            intChan <- 10
        }()
    
        result := <-intChan
        fmt.Printf("result:%d
    ", result)
    }
    
    func testNoBufChan() {
    
        var intChan chan int = make(chan int, 2)
        go func() {
    
            fmt.Printf("begin input to chan
    ")
            intChan <- 10
            intChan <- 10
            fmt.Printf("end input to chan
    ")
        }()
    
        //result := <- intChan
        //fmt.Printf("result:%d
    ", result)
        time.Sleep(10 * time.Second)
    }
    
    func main() {
        test()
        testNoBufChan()
    }
    View Code
    package main
    
    import (
        "fmt"
        "sync"
    )
    
    var waitGroup sync.WaitGroup
    
    func produce(ch chan<- string) { //生产者
        ch <- "hello1"
        ch <- "hello2"
        ch <- "hello3"
    
        close(ch) //关闭管道
        waitGroup.Done()
    }
    
    func consume(ch <-chan string) { //消费者
    
        for {
    
            str, ok := <-ch //判断管道是否关闭
            if !ok {
                fmt.Printf("ch is closed")
                break
            }
            fmt.Printf("value:%s
    ", str)
        }
        waitGroup.Done()
    }
    
    func main() {
        //生产者消费者模型
        var ch chan string = make(chan string)
        waitGroup.Add(2)
        go produce(ch)
        go consume(ch)
        waitGroup.Wait()
    }
    生产者消费者模型

     6. channel阻塞

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func sendData(ch chan string) {
        var i int
        for {
            var str string
            str = fmt.Sprintf("stu %d", i)
            fmt.Println("write:", str)
            ch <- str
            i++
        }
    }
    
    func main() {
        ch := make(chan string)
        go sendData(ch)
        time.Sleep(100 * time.Second)
    }
    channel阻塞

    7.chan之间的同步

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func sendData(ch chan string) {
        ch <- "Washington"
        ch <- "Tripoli"
        ch <- "London"
        ch <- "Beijing"
        ch <- "Tokio"
    }
    
    
    func getData(ch chan string) {
        var input string
        for {
            input = <-ch
            fmt.Println(input)
        }
    }
    
    
    func main() {
        ch := make(chan string)
        go sendData(ch)
        go getData(ch)
        time.Sleep(100 * time.Second)
    }
    View Code

    8.for range遍历chan

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func sendData(ch chan string) {
        ch <- "Washington"
        ch <- "Tripoli"
        ch <- "London"
        ch <- "Beijing"
        ch <- "Tokio"
    }
    
    func getData(ch chan string) {
        for input := range ch {
            fmt.Println(input)
        }
    }
    
    func main() {
        ch := make(chan string)
        go sendData(ch)
        go getData(ch)
        time.Sleep(100 * time.Second)
    }
    View Code

    9.chan的关闭  

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

      2.使用内置函数close进行关闭,chan关闭之后,没有使用for range的写法需要使用,v, ok := <- ch进行判断chan是否关闭 

    10.chan的只读和只写  

      a.只读chan的声明   

        Var 变量量的名字 <-chan int   

        Var readChan <- chan int

        b.只写chan的声明   

        Var 变量量的名字 chan<- int   

        Var writeChan chan<- int

    11. 对chan进行select操作
       Select {
           case u := <- ch1:
             case e := <- ch2:
             default: 
       }

    定时器 

    1、定时器的使用

    package main
    import (
        "fmt"
        "time"
    )
    
    
    func main() {
        t := time.NewTicker(time.Second)
        for v := range t.C {
            fmt.Println("hello, ", v)
        }
    }
    View Code

    2、一次定时器

    package main
    
    import (
        "fmt"
        "time"
    )
    
    
    func main() {
        select {
            Case <- time.After(time.Second):
            fmt.Println(“after”)
        }
    }
    View Code

    3、超时控制

    package main
    import (
        "fmt"
        "time"
    )
    func queryDb(ch chan int) {
        time.Sleep(time.Second)
        ch <- 100
    }
    func main() {
        ch := make(chan int)
    
        go queryDb(ch)
        t := time.NewTicker(time.Second)
        select {
            case v := <-ch:
            fmt.Println("result", v)
            case <-t.C:
            fmt.Println("timeout")
        }
    }
    View Code

     

    信号处理

    package main
    
    import (
        "fmt"
        "os"
        "os/signal"
        "sync"
        "syscall"
    )
    
    var waitGroup sync.WaitGroup
    
    func produce(ch chan<- string, exitChan chan bool) {
    
        var i int
        var exit bool
        for {
            str := fmt.Sprintf("hello %d", i)
            select { //select检测哪个管道可写或者可读
            case ch <- str:
            case exit = <-exitChan:
            }
            if exit {
                fmt.Printf("user notify produce exited
    ")
                break
            }
        }
        close(ch)
        waitGroup.Done()
    }
    
    func consume(ch <-chan string) {
    
        for {
            str, ok := <-ch
            if !ok {
                fmt.Printf("ch is closed")
                break
            }
            fmt.Printf("value:%s
    ", str)
        }
        waitGroup.Done()
    }
    
    func main() {
        // 在shell终端输入 kill -SIGUSR2 ID   给程序输入终止信号
        var ch chan string = make(chan string)
        var exitChan chan bool = make(chan bool, 1)
        var sinalChan chan os.Signal = make(chan os.Signal, 1)
        waitGroup.Add(2)
        signal.Notify(sinalChan, syscall.SIGUSR2)
        go produce(ch, exitChan)
        go consume(ch)
    
        <-sinalChan //读取然丢弃
        exitChan <- true
        waitGroup.Wait()
    
    }
    信号处理

    单元测试

    1.文件名必须以_test.go结尾
    2.使用go test -v执行单元测试

    calc.go

    package test
    
    //对函数进行单元测试
    func add(a int, b int) int {
        return a - b
    }
    
    func sub(a int, b int) int {
        return a - b
    }
    View Code

    calc_test.go

    package test
    
    import (
        "testing"
    )
    
    func TestAdd(t *testing.T) { //TestAdd必须大写的Test开头
        result := add(2, 8) //测试add函数
        if result != 10 {
            t.Fatalf("add is not right") //致命错误记录
            return
        }
    
        t.Logf("add is right") //记录一些常规信息
    }
    
    func TestSub(t *testing.T) {
        result := sub(2, 8)
        if result != -6 {
            t.Fatalf("add is not right")
            return
        }
    
        t.Logf("add is right")
    }
    View Code

     

     

     

     

  • 相关阅读:
    WPF e.Systemkey的一个坑
    Codeforces Round #375 (Div. 2) ABCDE
    主席树模板(poj2104)
    hdu5909-Tree Cutting(树形dp)
    HYSBZ 2243-染色 (树链剖分)
    POJ3237-Tree (树链剖分,线段树区间更新+点更新+区间查询)
    POJ2763-Housewife Wind(树链剖分)
    2016东北四省赛 小结
    HDU3966-Aragorn's Story(树链剖分)
    POJ 2749--Building roads(2-SAT)
  • 原文地址:https://www.cnblogs.com/domestique/p/8410313.html
Copyright © 2011-2022 走看看