Go并发


1.goroutine

package main

import(
    "fmt"
    "math"
    "sync"
)

func sum(id int) {
    var x int64
    for i := 0; i < math.MaxUint32; i++ {
        x+=int64(i)
    }
    fmt.Println(id,x)
}

func main() {
    wg:=new(sync.WaitGroup)
    wg.Add(2)

    for i := 0; i < 2; i++ {
        go func(id int) {
            defer wg.Done()
            sum(id)
        }(i)
    }
    wg.Wait()

}
输出:

0 9223372030412324865
1 9223372030412324865


2.runtime.Goexit()

package main

import(
    "fmt"
    //"math"
    "sync"
    "runtime"
)



func main() {
    wg:=new(sync.WaitGroup)
    wg.Add(1)

    go func() {
      defer wg.Done()
      defer fmt.Println("A.defer")

      func () {
        defer fmt.Println("B.defer")
        runtime.Goexit() //终止当前goroutine
        fmt.Println("B") //不会执行
      }()

      fmt.Println("A") //不会执行

    }()


    wg.Wait()

}

输出:

B.defer
A.defer

3.runtime.Gosched()

package main

import(
    "fmt"
    //"math"
    "sync"
    "runtime"
)



func main() {
    wg:=new(sync.WaitGroup)
    wg.Add(2)

    go func() {
      defer wg.Done()
      for i := 0; i < 6; i++ {
          fmt.Println(i)
          if i==3 {runtime.Gosched()}
      }
    }()

    go func() {
      defer wg.Done()
      fmt.Println("Hello,World!")
    }()

    wg.Wait()

}
输出:

0
1
2
3
Hello,World!
4
5

或者输出:

Hello,World!
0
1
2
3
4
5


4.channel

package main

import(
    "fmt"
    //"math"
    //"sync"
    //"runtime"
)



func main() {
    
    data:=make(chan int) //数据交换队列
    exit:=make(chan bool) //退出通知

    go func () {
      for d:=range data{ //从队列迭代接受数据,直到close
        fmt.Println(d)
      }
      fmt.Println("recv over!")
      exit<-true //发送退出通知

    }()

    data <- 1 //发送数据
    data <- 2
    data <- 3
    data <- 4
    data <- 5
    close(data) //关闭队列

    fmt.Println("send over.")
    <- exit //等待退出通知
}

输出:

1
2
3
4
5
recv over!
send over.

除用 range 外,还可用 ok-idiom 模式判断 channel 是否关闭。 

for {
    if d, ok := <-data; ok {
        fmt.Println(d)
    } else {
        break
    }
}

内置函数 len 返回未被读取的缓冲元素数量, cap 返回缓冲区大小。 


5.单向channel

可以将 channel 隐式转换为单向队列,只收或只发。 


c := make(chan int, 3)
var send chan<- int = c // send-only
var recv <-chan int = c // receive-only
send <- 1
// <-send // Error: receive from send-only type chan<- int
<-recv
// recv <- 2 // Error: send to receive-only type <-chan int

选择channel

package main

import(
    "fmt"
    "os"
    //"math"
    //"sync"
    //"runtime"
)



func main() {
    
    a:=make(chan int,3)
    b:=make(chan int)

    go func() {
      v,ok,s:=0,false,""
      for{
        select{ //随机选择可用的channel,接受数据
            case v,ok= <-a : s="a"
            case v,ok= <-b : s="b"
        }
        if ok{
            fmt.Println(s,v)
        }else{
            os.Exit(0)
        }
      }

    }()


    for i := 0; i<10; i++ {
        select{ //随机选择可用的channel,发送数据
        case a<-i:
        case b<-i:
        }
    }

    close(a)
    select{} //没有可用channel,阻塞main goroutine
}

输出:

a 0
b 4
a 1
b 6
a 2
a 3
b 9
a 5
a 7
a 8


6.模式
1.用简单工厂模式打包并发任务和channel
package main

import(
    "fmt"
    //"os"
    //"rand"
    "time"
    "math/rand" 
    //"sync"
    //"runtime"
)

//用简单工厂模式打包并发任务和channel

func NewTest() chan int {
    c:=make(chan int)
    rand.Seed(time.Now().UnixNano())//使用rand.Int 获取随机数,不加随机种子,每次遍历获取都是重复的一些数据
    go func() {
      time.Sleep(time.Second)
      c<-rand.Int()  
    }()
    return c
}

func main() {
   
   t:=NewTest()
   fmt.Println(<-t) 
   
}



2.用channel 实现信号量 (semaphore)



package main

import(
    "fmt"
    //"os"
    //"rand"
    //"time"
    //"math/rand" 
    "sync"
    //"runtime"
)


func main() {
   
   wg:=sync.WaitGroup{}
   wg.Add(3)

   sem:=make(chan int,1)

   for i := 0; i < 3; i++ {
       go func(id int) {
           defer wg.Done()

           sem <- 1 //向sem发送数据,阻塞或者成功

           for x := 0; x < 5; x++ {
               fmt.Println(id,x)
           }

           <- sem //接受数据,使得其他阻塞goroutine可以发送数据。

       }(i)
   }
   wg.Wait()
   
}

输出:
0 0
0 1
0 2
0 3
0 4
2 0
2 1
2 2
2 3
2 4
1 0
1 1
1 2
1 3
1 4

3.用closed channel 发送退出通知

package main

import(
    "fmt"
    //"os"
    //"rand"
    "time"
    //"math/rand" 
    "sync"
    //"runtime"
)


func main() {
   
   var wg sync.WaitGroup
   quit:=make(chan bool)

   for i := 0; i < 2; i++ {
       wg.Add(1)

       go func(id int) {
           defer wg.Done()

           task:=func() {
               fmt.Println(id,time.Now().Nanosecond())
               time.Sleep(time.Second)
           }

           for{
            select{
                case <-quit: //closed channel 不会阻塞,因此可用作退出通知
                    return
                default:   //执行正常的任务
                    task()
            }
           }


       }(i)
   }

   time.Sleep(time.Second*5) //让测试goroutine运行一会儿

   close(quit) //发出退出通知
   wg.Wait()
   
}

输出:

0 268850000
1 268850000
1 268850000
0 268850000
0 268850000
1 268850000
1 268850000
0 268850000
0 268850000
1 268850000
0 268850000

退出

4.用select实现超时(timeout),

5.channel 是第一类对象,可传参 (内部实现为指针) 或者作为结构成员。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值