第五章 Go语言并发

 一、goroute

   理论

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

    线程:线程是进程的一个执行实体,是CPU调度和分配的基本单位,是比进程跟小的能独立运行的基本单位

    进程与线程的关系:一个进程一颗创建和撤销多个线程,同一个进程中的多个线程之间可以并发执行

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

    协程和线程的关系:一个线程可以跑多个协程,协程是轻量级线程

    并发:同一时间段内执行多个操作

    并行:同一时刻执行多个操作

    Gorouteine多线程:线程是有操作系统进行管理,处于内核态,线程之间进行切换需要发生用户态到内核态的切换

             当系统中运行大量线程,系统会变得非常慢,用户态的线程支持大量线程的创建,也叫协程或者goroutine

  Goroutine使用介绍

//创建goroutine
func hello() {
    fmt.Println("hello goroutine")
}
func main() {
    go hello()
    fmt.Prinln("main thread terminate")
    time.Sleep(time.Second)    //goroutine会随着主线程的结束而结束,需要等待1s
}

//启动多个goroutine
func hello(i int) {
    fmnt.Println("hello goroutine",i)
}
func test() {
    for i := 0<10;i++{
        go hello(i)
    }
}
func main() {
    test()
    time.Sleep(time.Second)
}

//多核控制
var i int
func calc() {for {i++)}
func main() {
    cpu := runtime.NumCPU()    //显示当前CPU有多少核
    fmt.Prinln("cpu",cpu)
    runtime.GOMAXPROCS(1)    //只允许调用一个cpu内核
    for i := 0;i<10;i++ {
        go calc()
    }
    time.Sleep(time.Hour)
}

  Goroutine原理解析

    概念:一个操作系统线程对应用户态多个goroutine,可同时使用多个操作系统线程,操作系统线程对goroutine是多对多关系

    调度:当操作系统线程需要执行时,开始逐个执行goroutine

        蓝色goroutine执行结束后再执行灰色,当前有两个线程在执行

    系统调用的处理

        

 二、goroutine之间的通信

  全局变量和锁同步

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

  channel(队列管道)

    介绍:本质上是一个队列/容器,先进先出,类似unix中的管道pipe

    定义:需要指定勇气中元素的类型  var 变量名 chan 数据类型

var test chan int
var test chan string
var test chan map[string]string
var test chan stcurt
var test chan *stcurt    //定义一个地址,只能传入地址

    入队:a <- 100

    出队:data := <-a

//test1    
func main() {    
    var c chan int
    fmt.Printf("c =%v\n",c)
    c = make(chan int,10)    //如果不分配空间,则入队一直阻塞,运行报错
    fmt.Printf("c = %v\n",c)
    c <- 100        //<-c不给变量存储则为丢弃这个元素
    data := <-c
    fmt.Println("data",data)
}

//test2
type student struct {
    name string
}
func main() {
    var stuChan chan student
    stuChan = make(chan *Student,10)
    stu := student{name:"stu01"}
    stuChan <- stu
}

//test3
func produce(c chan int) {
    c <- 1000
    fmt.Println("produce finished")
}
func consume (c chan int) {
    data := <-c
    fmt.Println("data",data)
}
func main() {
    var c chan int
    fmt.Printf("c = %v\n",c)
    c = make(chan int)
    go produce(c)
    go consume(c)
    time.Sleep(time.Second * 3)
}    //不带缓冲区的队列,有出队的时候才能入队,否则入队失败

  channel练习

//chan和goroutine同步
func hello(c chan bool){
    fmt.Println("hello goroutine")
    c <- true
}
func main(){
    var exitChan chan bool
    exitChan = make(chan bool)
    go hello(exitChan)
    fmt.Println("main thread terminate")
    <-exitChan
}

//单向chan    
func sendData(sendch chan<- int) {    chan<- 表示之能入队
    sendch <- 10
}
func readData(sendch <-chan int) {    <- chan 表示只能出队
    data := <-sendch
    fmt.Println("data:", data)
}
func main() {
    chnl := make(chan int)
    go sendData(chnl)
    readData(chnl)
}

//chan关闭
func producer(chnl chan int) {
    for i := 0; i < 10; i++ {
        chnl <- i
    }
    close(chnl)    //关闭chan
}
func main() {
    ch := make(chan int)
    go producer(ch)
    for {
        v, ok := <-ch
        if ok == false {    //判断chan是否关闭,如果无法判断管道是否关闭会一直取出默认值0保持死循环    
            fmt.Println("chan is closed")
            break
        }
        fmt.Println("Received", v, ok)
    }
}


//for range
func produer(chnl chan int) {
    for i := 0; i < 10; i++ {
        chnl <- i
    }
    close(chnl)
}
func main() {
    ch := make(chan int)
    go produer(ch)
    for v := range ch {    //chan关闭无数据后自动运行结束
        fmt.Println("receive:", v)
    }
}

//带缓冲区的chanel
func write( ch chan int) {
    for i:= 0;i<5;i++ {
        ch <- i
        fmt.Println("successfully wrote",i,"to ch")
    }
    close(ch)
}
func main() {
    ch := make(chan int,2)
    go write(ch)
    time.Sleep(2 * time.Second)
    for v := range ch {
        fmt.Println("read value",v,"from ch")
        time.Sleep(2 *time.Second)
    }
}

//channel的长度和容量
func main() {
    ch := make(chan string,3)
    ch <- "naveen"
    ch <- "paul"
    fmt.Println("capacity is",cap(ch))
    fmt.Println("length is",len(ch))
    fmt.Println("read value",<-ch)
    fmt.Println("new length is",len(ch))
}

//waitgroup等待一组goroutine结束,使用不带缓冲区的channel实现
func process(i int,ch chan bool) {
    fmt.Println("started Goroutine",i)
    time.Sleep(2 * time.Second)
    fmt.Printf("Goroutine %d ended\n",i)
    ch <- true
}
func main() {
    no := 3
    exitChan := make(chan bool,no)
    for i := 0;i<no;i++ {
        go process(i,exitChan)
    }
    for i:= 0;i<no;i++ {
        <exitChan
    }
    fmt.Println("All goroutines finshed executing")
}
//使用sync-WaitGroup实现
func process(i int,wg *sync.WaitGroup) {
    fmt.Println("started Goroutine",i)
    time.Sleep(2 * time.Second)
    fmt.Printf("Goroutine %d ended\n",i)
    wg.Done()    //wg-1
}
func main() {
    no := 3
    var wq sync.WaitGroup    //定义一个wgh,默认为0
    for i := 0; i<no; i++ {
    wg.Add(1)    wg+1
    go process(i,&wg)
}
    wg.Wait()    //当wg为0时才会向下执行
    fmt.Println("All goroutines finished executing“)
}”

 

 三、select和线程之间的安全

  select语义介绍

    定义:多channel操作,同时监听一个或多个channel,直到其中一个channel ready,如果其中合格多个chgannel同时ready,随机选择一个进行操作,语法和weitch case类似,只获取一个数值就结束

func server1(ch chan string) {
    time.Sleep(time.Second * 3)
    ch <- "response from server1"
}
func server2(ch chan string) {
    time.Sleep(time.Second)
    ch <-"response from server2")
}
func main() {
    output1 := make(chan string)
    output2 := make(chan stirng)
    go server1(output1)
    go server2(output2)
    //s1 := <-output1    //等待3s
    //fmt.Println("s1:",s1)
    //s2 := <-output2    //等待1s后等待s1结束再执行
    //fmt.Println("s2",s2)
    select {    //最终只取一个channel的结果
    case s1 := <-output1:
        fmt.Println("s1:",s1)
    case s2 := <-output2:
        fmt.Prinln("s2:",s2)
    }
}

    default分支:放case分支的channel都没有ready的话,执行default,用来判断channel是否满了或者为空

//test1    
func server1(ch chan string) {
    time.Sleep(time.Second * 3)
    ch <- "resdponse from server1"
}
func server2(ch chan string) {
    time.Sleep(time.Second)
    ch <- "response from server2"
}
func main() {
    output1 := make(chan string)
    output2 := make(chan string)
    go server1(output1)
    go server2(output2)
    select {    //最终只取一个channel的结果
    case s1 := <- output1:
        fmt.Println("s1;",s1)
    case s2 := <-output2:
        fmt.Println("s2:",s2)
    default:
        fmt.Println("run default")    //因为s1和s2需要等待,default瞬间执行
    }
}

//test2
func write(ch chan string) {
    for {
        select {
        case ch <- "hello":
            fmt.,Println("write succ")
        default:
            fmt.Println("channel is full")
        }
        time.Sleep(time.Millisecond * 500)
    }
}
func main() {
    output1 := make(chan string,10)
    go write(output1)
    for s := range.output1 {
        fmt.Println("recv:"s)
        time.Sleep(time.Second)
    }
}

  线程安全介绍

 

  互斥锁介绍

  读写锁介绍

  原子操作介绍 

 四、网络编程

猜你喜欢

转载自www.cnblogs.com/parallel-Y/p/11432988.html