简介

go

配置

GOMAXPROCS 设置逻辑CPU数量,个别状况下应用CPU外围数量值.应用足够的线程来进步golang的并行执行效率.如果你的业务是IO密集型则能够设置数倍与CPU外围数的值来失去更好的性能.如果Go程序在容器中执行则须要依据状况缩小该值得,因为容器中无奈应用到宿主机的全副外围.设置更小的值能够防止线程切换的开销.

应用channel进行协程通信

定义通道

ch1 := make(chan string) //定义了一个无缓冲的string通道
ch2 := make(chan string , 4) //定义了一个4个元素的string通道

通道操作符

ch1 <- "Chengdu" //向通道写入数据
itemOfCh1 := <- ch1 //从ch1通道读取一条数据
<- ch1 //读取通道的下一个值
var in_only chan<- int //只能接管通道
var out_only <-chan int //只读取的通道
close(ch) //敞开通道

通道阻塞

默认状况通道是同步无缓冲的,在接受方未筹备好之前发送方是阻塞的.通道中没有数据则接管方也是阻塞的.

package main

import (
    "fmt"
    "time"
)

func f1(in chan int) {
    data := <-in
    fmt.Println(data)
}

func main() {
    out := make(chan int)
    out <- 2
    fmt.Println(v)
    go f1(out)
    time.Sleep(100 * time.Millisecond)
}
批改 out:=make(chan int , 1)

应用信号量

能够通过信号量来让主协程期待子协程的实现退出执行.

package main

import (
    "fmt"
    "time"
)

func f1(in chan int, done chan int) {
    data := <-in
    fmt.Println(data)
    time.Sleep(10e9)
    done <- 1
}

func main() {
    out := make(chan int)
    done := make(chan int)
    go f1(out, done)
    out <- 2
    <-done
}

输入 2 之后10秒后程序才会退出,咱们就不须要应用sleep来让主过程执行.

敞开通道

显式的敞开通道,敞开通道示意发送者不会有新的数据发送给接受者了.只有发送者须要敞开通道.

ch := make(chan int )
defer close(ch)

data,ok := <-ch //接管到数据则ok为 true,应用ok能够检测通道是否敞开或者阻塞

上面这种状况,读取通道在主过程不会报死锁谬误,因为查看到通道敞开后就不进行通道读取跳出循环,因而不会再继读没有写入的通道.所以没有死锁.

package main

import "fmt"

func makeStream(n int) chan bool {
    ch := make(chan bool, n)
    go func() {
        for i := 0; i < n; i++ {
            ch <- true
        }
        close(ch)
    }()
    return ch
}

func main() {
    stream := makeStream(5)

    for {
        v, ok := <-stream
        if !ok {
            break
        }
        fmt.Println(v)
    }
}

应用select 切换协程

selectfor
default
package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        for i := 0; i < 10; i++ {
            ch1 <- fmt.Sprintf("A%d", i)
        }
    }()

    go func() {
        for i := 0; i < 10; i++ {
            ch2 <- fmt.Sprintf("B%d", i)
        }

    }()

    go func() {
        for {
            select {
            case v := <-ch1:
                fmt.Println(v)
            case v := <-ch2:
                fmt.Println(v)
            }
        }
    }()

    time.Sleep(1e9)
}

能够应用这种模式做为服务端来循环解决客户申请

计时器(Ticker)

type Ticker struct {
    C <-chan Time // The channel on which the ticks are delivered.
    r runtimeTimer
}

定时器的C变量会依据你创立的定时器工夫,在给定工夫外向该通道写入工夫

package main

import (
    "fmt"
    "time"
)

func main() {
    t := time.NewTicker(time.Second)

    go func() {
        for {
            v := <-t.C
            fmt.Println(v)
        }
    }()

    time.Sleep(10e9) // <-time.After(10e9) 应用通道来设置超时
}
time.Tick(duration)time.NewTicker(1e9).Ctime.After(duration)

协程的复原

panic
package main

import (
    "log"
    "time"
)

func doWork() {
    time.Sleep(4e9)
    panic("fk")
}

func main() {

    go func() {
        for {
            log.Printf("another worker")
            time.Sleep(1e9)
        }
    }()

    go func() {

        defer func() {
            if err := recover(); err != nil {
                log.Printf("出问题了 %s", err)
            }
        }()

        doWork()
    }()

    time.Sleep(10e9)
}

应用锁还是通道

在一种场景下,有多个工作,一个worker解决一项工作.这种场景很适宜应用通道和协程来解决问题

package main

type Task struct{}
type Result struct{}

func process(Task *Task) Result {
    return Result{}
}

func main() {

    tasks, results := make(chan Task), make(chan Result)

    workCount := 10

    //创立工作
    go func() {
        for i := 0; i < workCount; i++ {
            tasks <- Task{}
        }
    }()

    //启动worker
    for i := 0; i < workCount; i++ {
        go func() {
            for {
                t := <-tasks
                result := process(&t) //解决数据
                results <- result     //写入构造
            }
        }()
    }

    //生产后果

}
  • 应用锁的情景:

    • 访问共享数据结构中的缓存信息
    • 保留应用程序上下文和状态信息数据
  • 应用通道的情景:

    • 与异步操作的后果进行交互
    • 散发工作
    • 传递数据所有权