##Day05 :

🌍并发编程🌏

goroutine :

轻量级“线程“;

非抢占式多任务处理,由协程主动交出控制权;

编译器/解释器/虚拟机层面的多任务,具体执行由调度器完成;

多个协程可以在一个或多个线程上运行;

func main(){   
    var a [10] int
    for i := 0 ;i<10 ;i++{
        go func(i int){
            for{            
                a[i]++
                //runtime.Gosched()  //交出控制权,让其他goroutine有运行机会
            }      
        }(i)
    }   
    time.Sleep(time.Millisecond)
    fmt.Println(a)
}

上面的代码简单展示了协程的工作方式,在函数前加上关键字go,表明这个函数交给main协程以外的协程由调度器执行,调度器会在合适的点进行切换。而关于协程相较于线程的轻量级,在于其主协程(main)和其他协程(…goroutine)可以相互访问。如下图:

协程Coroutine

Go语言原生支持协程,Java的一些第三方的JVM对协程支持。

goroutine可能切换的点:

I/O,select

channel

等待锁

函数调用

runtime.Gosched()

⚠️多个协程会映射到一个线程里

channel :

🍨Go语言创造者:

''不要通过共享内存来通信;通过通信来共享内存’’

import (   
    "fmt"
    "time"
)
func worker(id int , c chan int) {
    for{
        n,ok := <-c
        if !ok{
            break
        }
        fmt.Printf("Worker %d receive %d\n",id,n)
    }
}
func createWorker(id int)chan <- int {		// <- 定义这个channel是用来发数据的
    c := make(chan int)
    go worker(id,c)
    return c
}
func channelDemo(){
    //c == nil
    //channel of int  channel里面的内容是int   
    var channels [10] chan <- int   
    for i := 0; i<10 ; i++{
        channels [i] = createWorker(i)
    }   
    for i:= 0 ; i<10 ;i++{
        channels[i] <- 'A'+i   
    }   
    //n := <-c             //收数据   
    //fmt.Println(s)            //直接运行会报deadlock
    time.Sleep(time.Millisecond)
}
func bufferedChannel(){   //只有发没有收的channel会产生死锁
    n := make(chan int , 3)  //设置缓冲区,不会产生deadlock
    go worker(0,n)
    n <- 'a'
    n <- 'b' 
    n <- 'c'
    time.Sleep(time.Millisecond)
}
func channelClose(){
    n := make(chan int , 3)		//设置缓冲区,不会产生deadlock
    go worker(0,n)
    n <- 'a'
    n <- 'b'
    n <- 'c'
    close(n)    //会收到许多空数据
    time.Sleep(time.Millisecond)
}
func main(){
    // channelDemo()
    // bufferedChannel()   //缓冲区在提升性能上是有帮助的   
    channelClose()
}

使用channel等待goroutine结束:

import (   
    "fmt"
    "sync"
)
func doWork(id int , c chan int , wg *sync.WaitGroup) {
    for n := range c{
        fmt.Printf("Worker %d receive %c\n",id,n)
        wg.Done()   
    }
}
type worker struct {
    in chan int
    wg *sync.WaitGroup
}
func createWorker(id int, wg *sync.WaitGroup) worker {
    w := worker{
        in: make(chan int),
        wg: wg,
    }
    go doWork(id, w.in , wg)
    return w
}
func channelDemo(){
    var wg sync.WaitGroup     //等待多人完成任务
    var workers [10] worker
    for i := 0; i<10 ; i++{
        workers [i] = createWorker(i,&wg)
    }
    wg.Add(20)
    for i, worker := range  workers{
        worker.in <- 'A'+i
    }
    for i ,worker := range workers{
        worker.in <- 'a'+i
    }
    wg.Wait()   
    //wait for all of them
    //time.Sleep(time.Millisecond)
}

func main(){
    channelDemo()
}

使用channel实现树的遍历:

func (node *Node)TraverseWithChannel() chan *Node{		//channel里面一个一个节点去收
    out := make(chan *Node)
    go func() {
        node.TraverseFunc(func(node *Node) {
            out <- node
        })
        close(out)
    }()
    return out
}

select :

👊select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接收。

👊select 随机执行一个可运行的 case。如果没有 case 可运行,它将阻塞,直到有 case 可运行。一个默认的子句应该总是可运行的。

func generator() chan int{
    out := make(chan int)
    go func() {
        i := 0
        for{
            time.Sleep(time.Duration(rand.Intn(1500))*
                       time.Millisecond)
            out <- i
            i++
        }
    }()
    return out
}
func main() {
    var c1, c2 = generator(),generator()
    for {
        select { 		//非阻塞
            case n := <-c1:
            fmt.Println("Receive from c1:", n)
            case n := <-c2:
            fmt.Println("Receive from c2:", n)
        }
    }
}

程序输出结果:

selectTest

传统同步机制:

WaitGroup

Mutex (互斥量)

Cond

以Mutex为例:

type atomicInt struct {
    value int
    lock sync.Mutex//系统提供原子化操作,加入锁,实现原子化的int
}
func (a *atomicInt) increment(){
    fmt.Println("safe increment")
    func(){
        a.lock.Lock()
        defer a.lock.Unlock()
        a.value++   
    }()
}
func(a *atomicInt) get() int{
    a.lock.Lock()
    defer a.lock.Unlock()
    return a.value
}
func main() {
    var a atomicInt
    a.increment()
    go func() {
        a.increment()
    }() 
    time.Sleep(time.Millisecond)
    fmt.Println(a.get())		//引入锁机制之后,DATA RACE没有了
}

Mutex锁


##Day06 :

http标准库 :

⬛️使用http客户端发送请求;

⬛️使用http.Client控制请求头;

⬛️使用httputil简化工作;

func main() {
    request, err := http.NewRequest(http.MethodGet,"http://www.klayhu.github.io", nil)  
    request.Header.Add("User-Agent","Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1")
    client := http.Client{
        //如果没有请求头就不需要再次重定向
        CheckRedirect: func(req *http.Request,
                            via []*http.Request)
        error {
            //重定向的路径都在via里,每次的目标都在request里面,如果返回错误就会终止重定向  ---见源码
            fmt.Println("Redirect:",req)
            return nil
        },
    }
    resp , err := client.Do(request)
    if err != nil{
        panic(err)
    }
    defer resp.Body.Close()
    s, err := httputil.DumpResponse(resp, true)
    if err != nil{
        panic(err)
    }
    fmt.Printf("%s\n",s)
}

交由系统重定向打印结果



Go

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!