zoukankan      html  css  js  c++  java
  • go 并发编程

    进程

    线程

    协程

    设置golang运行cpu数

    1.主线程和协程同时执行

    package main
    
    import (
        "fmt"
        "strconv"
        "time"
    )
    
    func test(){
        for i:=1;i<=10;i++{
            fmt.Println("test() hello world"+strconv.Itoa(i))
            time.Sleep(time.Second)
        }
    }
    
    //开启协程
    func main(){
        go test()//开启一个协程
        for i:=1;i<=10;i++{
            fmt.Println("main() hello,goang"+strconv.Itoa(i))
            time.Sleep(time.Second)
        }
    
    }
    //主线程和协程同时执行
    View Code

     2.设置golang运行cpu数

    package main
    
    import (
        "fmt"
        "runtime"
    )
    
    //设置golang运行cpu数
    func main(){
    
        //获取当前系统cpu数量
        num:=runtime.NumCPU()
        //我这里设置num-1的cpu运行go程序
        runtime.GOMAXPROCS(num)
        fmt.Println("num=",num)
    }
    View Code

    3.加锁

    package main
    
    import (
        "fmt"
        "sync"//互斥锁
    )
    
    //求1-n每个数的阶乘
    var (
        myMap = make(map[int]int, 10)
        lock sync.Mutex
    )
    
    func test(n int) {
        res := 1
        for i := 1; i <= n; i++ {
            res *= i
        }
        //存入map
        //加锁
        lock.Lock()
        myMap[n] = res
        lock.Unlock()
    }
    
    func main() {
        for i := 1; i <= 60; i++ {
            go test(i)
        }
        //输出
        //加锁避免还未生成完就进行输出
        lock.Lock()
        for i, v := range myMap {
            fmt.Printf("map[%d]=%d
    ", i, v)
        }
        lock.Unlock()
    }
    View Code

    管道

     1.管道说明

    package main
    
    import "fmt"
    
    //管道
    func main() {
        //1.创建一个存放3个int类型的管道
        var intChan chan int
        intChan = make(chan int, 3)
        //看看intChan是什么
        fmt.Printf("intChan 的值=%v intChan 本身的地址=%p
    ", intChan, &intChan)
    
        //3.向管道写入数据
        intChan <- 10
        num := 211
        intChan <- num
        intChan <- 50
        //intChan<-98//当我们写入数据时,不能超过其容量
    
        //4.看看管道长度和cap(容量)
        fmt.Printf("channel len=%v cap=%v
    ", len(intChan), cap(intChan))
        //5.从管道中读取数据
        var num2 int
        num2=<-intChan
        fmt.Println("num2=",num2)
        fmt.Printf("channel len=%v cap=%v
    ",len(intChan),cap(intChan))
        //6.在没有使用协程的情况下,如果我们的管道数据已经全部取出,再取就会报deadlock
        num3:=<-intChan
        num4:=<-intChan
        num5:=<-intChan
    //会报错
        fmt.Println("num3=",num3,"num4=",num4,"num5=",num5)
    }
    View Code

    2.管道的读写演示

    package main
    
    import (
        "fmt"
    )
    
    func c1() {
        var intChan chan int
        intChan = make(chan int, 3)
        intChan <- 10
        intChan <- 20
        intChan <- 10
    
        num1 := <-intChan
        num2 := <-intChan
        num3 := <-intChan
        fmt.Printf("num1=%v num2=%v num3=%v", num1, num2, num3)
    }
    
    //存放map
    
    func c2() {
        var mapChan chan map[string]string
        mapChan = make(chan map[string]string, 10)
        m1 := make(map[string]string, 20)
        m1["city1"] = "北京"
        m1["city2"] = "天津"
    
        m2 := make(map[string]string, 20)
        m2["hero1"] = "宋江"
        m2["hero2"] = "武松"
        mapChan <- m1
        mapChan <- m2
    
    }
    
    //结构体变量
    type Cat struct {
        Name string
        Age  int
    }
    
    func chanStruct() {
        var catChan chan Cat
        catChan = make(chan Cat, 10)
        cat1 := Cat{Name: "tom1", Age: 18}
        cat2 := Cat{Name: "tom2", Age: 28}
        catChan <- cat1
        catChan <- cat2
        fmt.Println(cat1, cat2)
    }
    
    func chanStruct2() {
        var catChan chan *Cat
        catChan = make(chan *Cat, 10)
    
        cat1 := Cat{Name: "tom1", Age: 18,}
        cat2 := Cat{Name: "tom2", Age: 28,}
        catChan <- &cat1
        catChan <- &cat2
        //取出
        cat11 := <-catChan
        cat22 := <-catChan
        fmt.Println(cat11, cat22)
    }
    
    //存放任意数据类型
    func chanOther() {
        var allChan chan interface{}
        allChan = make(chan interface{}, 10)
    
        cat1 := Cat{Name: "tom1", Age: 19,}
        cat2 := Cat{Name: "tom2", Age: 29,}
        allChan <- cat1
        allChan <- cat2
        allChan <- 10
        allChan <- "jack"
    
        //取出
    
        cat11 := <-allChan
        cat22 := <-allChan
        v1 := <-allChan
        v2 := <-allChan
        fmt.Println(cat11, cat22, v1, v2)
    }
    
    //取出结构体属性需要使用类型断言
    func c6() {
        var allChan chan interface{}
        allChan = make(chan interface{}, 10)
    
        cat1 := Cat{Name: "tom1", Age: 19,}
        cat2 := Cat{Name: "tom2", Age: 29,}
        allChan <- cat1
        allChan <- cat2
        allChan <- 10
        allChan <- "jack"
    
        //取出
    
        cat11 := <-allChan
        //使用类型断言,恢复结构体形式
        a := cat11.(Cat)
        fmt.Println(a.Name)
    }
    
    //管道的读写演示
    func main() {
        c6()
    }
    View Code

    3.channel遍历以及关闭

    package main
    
    import "fmt"
    
    //channel遍历以及关闭
    func chanClose(){
    
        intChan :=make(chan int,3)
        intChan<-100
        intChan<-200
        close(intChan)//关闭
        //关闭后,不能继续写入,但可以读取
    }
    //channel遍历
    //1.遍历时如果没有关闭,则会出现deadlock错误
    //2.如果已经关闭,遍历完成后,就会退出
    func chanFor(){
        intChan2:=make(chan int,100)
        for i:=0;i<100;i++{
            intChan2<-i*2
        }
        //遍历管道不能使用for
        close(intChan2)
        for v:=range intChan2{
            fmt.Println("v=",v)
        }
    }
    
    func main(){
    
    chanFor()
    }
    View Code

    4.最佳案例1使用协程

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func writeData(intChan chan int) {
        for i := 1; i <= 50; i++ {
            //放入数据
            intChan <- i
            fmt.Println("writeData", i)
        }
        close(intChan)
    }
    
    func readData(intChan chan int, exitChan chan bool) {
        for {
            v, ok := <-intChan //接收读取的内容,错误
            //取完之后再取,返回false,
            //fmt.Printf("布尔值 %v
    ",ok)
            if !ok {
                break
            }
    
            fmt.Printf("readData 读取到数据=%v
    ", v)
        }
        //标记任务完成
        exitChan <- true
        close(exitChan)
    }
    
    func main() {
        //创建两个管道
        intChan := make(chan int, 50)
        exitChan := make(chan bool, 1)
        //启用协程
        go writeData(intChan)
        go readData(intChan, exitChan)
        time.Sleep(time.Second * 1) //1秒
        //每次到这里检查是否读完了
        for {
            _, ok := <-exitChan
            if !ok {
                fmt.Println("结束了")
                break
            }
        }
    }
    View Code

    5.多协程取素数

    package main
    
    import (
        "fmt"
        "time"
    )
    
    //统计1-n素数
    
    //向intChan放入1-n个数
    func putNum(intChan chan int) {
        for i := 1; i <= 8000; i++ {
            intChan <- i
        }
        close(intChan)
    }
    
    //取出数据判断是否为素数,如果是就放书primeChan
    func primeNum(intChan chan int, primeChan chan int, exitChan chan bool) {
        //使用for循环
        var flag bool
        for {
            time.Sleep(time.Millisecond * 10)
            num, ok := <-intChan
            if !ok {
                break
            }
    
            flag = true
            for i := 2; i < num; i++ { //不是素数
                if num%i == 0 {
                    flag = false
                    break
                }
            }
            if flag {
                //素数存入
                primeChan <- num
            }
        }
        fmt.Println("有一个primeNUm 协程,因为取不到数据,退出")
        //标记读取完成
        exitChan <- true
    }
    func main() {
        intChan := make(chan int, 1000)
        primeChan := make(chan int, 2000)
        //标识退出的管道
        exitChan := make(chan bool, 4)
        //开启一个协程,向intChan放入1-8000个数
        go putNum(intChan)
        //开启四个协程,从intChan取出数据,并判断是否为素数,如果是就放入
        for i := 0; i < 4; i++ {
            go primeNum(intChan, primeChan, exitChan)
        }
        //这里主线程 处理
        go func() {
            for i := 0; i < 4; i++ {
                <-exitChan
            }
            close(primeChan)
        }()
        //遍历primeChan,把结果取出
        for{
            res,ok:=<-primeChan
            if !ok{
                break
            }
    
            //输出
            fmt.Printf("素数=%d
    ",res)
        }
        fmt.Println("线程退出")
    
    }
    View Code

    6.只读,只写

    //1.channel可以生名为只读,或者只写性质
    func g1(){
    
        //1.可读可写
        //var chan1 chan int
        //2.声明为只写
        var chan2 chan<-int
        chan2=make(chan int,3)
        chan2<-20
        fmt.Println("chan2=",chan2)
        //3.声明为只读
        //var chan3  <-chan int
        //num2:=<-chan3
    }
    View Code

    7.测试只读,只写

    package main
    
    import (
        "fmt"
    )
    
    //1.channel可以生名为只读,或者只写性质
    func g1() {
    
        //1.可读可写
        //var chan1 chan int
        //2.声明为只写
        var chan2 chan<- int
        chan2 = make(chan int, 3)
        chan2 <- 20
        fmt.Println("chan2=", chan2)
        //3.声明为只读
        //var chan3  <-chan int
        //num2:=<-chan3
    }
    
    //只写
    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
            }
        }
        fmt.Println("结束。。。")
    
    }
    View Code

    8.使用select可以解决从通道取数据阻塞问题

    package main
    
    import (
        "fmt"
        "time"
    )
    
    //使用select可以解决从通道取数据阻塞问题
    
    func main(){
        //定义一个管道10个数据int
        intChan:=make(chan int,10)
        for i:=0;i<10;i++{
            intChan<- i
        }
        //2.定义一个管道5个数据string
        stringChan:=make(chan string,10)
    
        for i:=0;i<5;i++{
            stringChan<-"hello"+fmt.Sprintf("%d",i)
        }
        //传统方法在遍历管道时,如果不关闭会阻塞导致deadlock
        //问题,在实际开发中我们不好确定什么关闭管道
        //可以使用select方式可以解决
        //label:
        for{
            select{
            //注意:这里,如果intChan一直没有关闭,不会一直阻塞而deadlock
            //会自动到下一个case匹配
            case v :=<-intChan:
                fmt.Printf("从intChan读取的数据%d
    ",v)
            time.Sleep(time.Second)
                case v:=<-stringChan:
                    fmt.Printf("从stringChan读取的数据%v
    ",v)
                    time.Sleep(time.Second)
            default:
                fmt.Printf("都取不到了,程序可以加入逻辑
    ")
                time.Sleep(time.Second)
                return
                //break label
            }
        }
    }
    View Code

    9.goroutine中使用recover,解决协程中出现panic ,导致程序崩溃

    package main
    
    import (
        "fmt"
        "time"
    )
    
    //goroutine中使用recover,解决协程中出现panic ,导致程序崩溃
    
    //
    func sayHello() {
        for i := 0; i < 10; i++ {
            time.Sleep(time.Second)
            fmt.Println("hello world")
        }
    }
    
    func test() {
        defer func() {
            //捕捉test抛出的panic
            if err := recover(); err != nil {
                fmt.Println("test() 发生错误", err)
            }
        }()
        //定义一个map
        var myMap map[int]string
        myMap[0] = "golang"
    
    }
    
    func main() {
        go sayHello()
        go test()
        for i := 0; i < 10; i++ {
            fmt.Println("main() ok=", i)
            time.Sleep(time.Second)
        }
    }
    View Code

    借助scanner阻塞主线程结束

    func main(){
    go func() {
        var times int
        for{
            times++
            fmt.Println("tick",times)
            time.Sleep(time.Second)
        }
    }()
    //不输入实际对主协程进行了阻塞
    var input string
    fmt.Scanln(&input)
    }
    View Code

     多个子协程调用顺序是随机的

    func printNum() {
        for i := 1; i < 5; i++ {
            time.Sleep(250 * time.Millisecond)
            fmt.Printf("%d", i)
        }
    }
    
    func printLetter() {
        for i := 'a'; i < 'e'; i++ {
            time.Sleep(400 * time.Millisecond)
            fmt.Printf("%c", i)
        }
    }
    func main() {
        go printNum()
        go printLetter()
        time.Sleep(3 * time.Second)
        fmt.Println("
     main over")
    
    }
    View Code

     生产消费模型

    //借助channel实现生产消费模型
    func main() {
        ch1 := make(chan int)
        //生产
        go producer(ch1)
        //消费
        ch_bool1 := make(chan bool)
        ch_bool2 := make(chan bool)
        ch_bool3 := make(chan bool)
        go customer("jack", ch1, ch_bool1)
        go customer("rose", ch1, ch_bool2)
        go customer("anner", ch1, ch_bool3)
        <-ch_bool1
        <-ch_bool2
        <-ch_bool3
        fmt.Println("main over")
    }
    
    func producer(ch chan int) {
        for i := 1; i <= 10; i++ {
            ch <- i
            fmt.Printf("生产%d号面包
    ", i)
            time.Sleep(time.Second)
        }
        close(ch)
    }
    
    //消费面包
    func customer(name string, ch chan int, ch_bool chan bool) {
        for data := range ch {
            fmt.Printf("%v吃了%d号面包
    ", name, data)
            //    time.Sleep(time.Second)
        }
        ch_bool <- true
        close(ch_bool)
    }
    View Code

     timer

    延迟存入通道

    func main(){
        //创建计时器
        //4秒后存入通道
        timer1:=time.NewTimer(4*time.Second)
        fmt.Println(time.Now())
        data:=<-timer1.C//取到之前会被阻塞
        fmt.Printf("timer_t=%T
    ",timer1.C)
        fmt.Printf("data=%T
    ",data)
        fmt.Println("da",data)
    }
    View Code

    after延迟存入

    //after使用
    //延迟存入通道
    func main() {
        //2.使用After(),返回值<-chan Time,同Timer.C
        ch1 := time.After(5 * time.Second)
        fmt.Println(time.Now())
        data := <-ch1
        fmt.Printf("data_type=%T
    ", data)
        fmt.Println("data", data)
    }
    View Code

    sync

    防止主线程先于子goroutine结束

    func main() {
        var wg sync.WaitGroup
        fmt.Printf("%T
    ", wg)
        wg.Add(3)
        rand.Seed(time.Now().UnixNano())
        go printNum(&wg, 1)
        go printNum(&wg, 2)
        go printNum(&wg, 3)
        wg.Wait() //进入阻塞状态
        defer fmt.Println("main over")
    }
    func printNum(wg *sync.WaitGroup, num int) {
        for i := 1; i <= 3; i++ {
            pre := strings.Repeat("	", num-1)
            fmt.Printf("%s第%d号子goroutine,%d
    ", pre, num, i)
            time.Sleep(time.Second)
        }
        wg.Done()
    }
    View Code

     互斥锁实现购票

    //互斥锁
    var tickets int = 20
    var wg sync.WaitGroup
    var mutex sync.Mutex
    
    func main() {
        wg.Add(4)
        go saleTickets("1号窗口", &wg)
        go saleTickets("2号窗口", &wg)
        go saleTickets("3号窗口", &wg)
        go saleTickets("4号窗口", &wg)
        wg.Wait()
        defer fmt.Println("所有車票售空")
    }
    
    func saleTickets(name string, wg *sync.WaitGroup) {
        defer wg.Done()
        for {
            //锁定
            mutex.Lock()
            if tickets > 0 {
                time.Sleep(1 * time.Second)
                //获取窗口的编号
                num, _ := strconv.Atoi(name[:1])
                pre := strings.Repeat("--", num)
                fmt.Println(pre, name, tickets)
                tickets--
            } else {
                fmt.Printf("%s 结束售票
    ", name)
                mutex.Unlock()
                break
            }
            //解锁
            mutex.Unlock()
        }
    }
    View Code

    读写互斥锁

    //读写互斥锁
    func main() {
        var rwm sync.RWMutex
        for i := 1; i <= 3; i++ {
            go func(i int) {
                fmt.Printf("goroutine %d,尝试读锁定。
    ", i)
                rwm.RLock()
                fmt.Printf("goroutine %d 已经锁定了
    ", i)
                time.Sleep(5 * time.Second)
                fmt.Printf("goroutine %d,读解锁", i)
                rwm.RUnlock()
            }(i)
        }
    
        time.Sleep(1 * time.Second)
        fmt.Println("main.,尝试写锁定")
        rwm.Lock()
        fmt.Println("main 已经锁定了")
        rwm.Unlock()
        fmt.Println("main 写解锁")
    }
    View Code

    解决资源竞争

  • 相关阅读:
    详解SQL Server的两个存储过程:sp_MSforeachtable/sp_MSforeachdb
    使用WorkService创建定时任务
    Mahout下个性化推荐引擎Taste介绍
    Apache Mahout中的机器学习算法集
    内网信息收集笔记 楼下的小可怜
    关于linux的suid提权 楼下的小可怜
    Cobalt Strike初探 楼下的小可怜
    Google hacking 楼下的小可怜
    Git和Repo扫盲——如何取得Android源代码 zt
    Howto find native code memory leak in Android
  • 原文地址:https://www.cnblogs.com/huay/p/12171695.html
Copyright © 2011-2022 走看看