Go语言学习笔记7-并发

并发

  并发指在同一时间内执行多个任务,GO语言的并发通过goroutine特效完成。goroutine的概念类似于线程,但goroutine由Go程序运行时调度和管理,GO程序会智能地将goroutine中任务合理地分配给每个CPU。在程序启动时,GO程序就会为main()函数创建一个默认的goroutine。

go 函数名 (参数列表)

go func () {
 函数体
} (调用参数列表)

通道

  1. Go语言中的通道channel是一种特殊的类型。在任何时候,同时只能有一个goroutine访问通道进行发送和获取数据。goroutine间通过通道就可以通信。

var 通道变量 chan 通道类型
通道实例 := make(chan 通道 数据类型)

  • 通道的发送使用特殊操作符<-, 通道变量 <- 值
  • 通道接收同时使用<-操作符,接收值 := <- 通道
  • 通道的收发操作在不同的两个goroutine间进行
  • 通道的收发将持续阻塞直到发送方发送数据或数据被接收
  • 每次只接收或发送一个元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import (
"fmt"
"time"
)

func main() {
ch := make(chan int)

go func() {
for i := 3; i >= 0; i-- {
ch <- i

time.Sleep(time.Second)
}
}()

//遍历接收通道数据
for data := range ch {
fmt.Println(data)

if data == 0 {
break
}
}
}
  1. Go的通道可以在申明时约束其操作方向, 如只是发送或是接收。这种被约束方向的通道被称做单向通道.
    var 通道实例 chan<- 元素类型
    var 通道实例 <-chan 元素类型

  2. 在无缓冲通道的基础上,为通道增加一个有限大小的存储空间形成带缓冲通道,带缓冲通道在发送时无需等待接收方接收即可完成发送过程,并且不会发生阻塞,只有当存储空间满时才会发生阻塞。
    通道实例 := make(chan 通道类型, 缓冲大小)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"fmt"
)

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

fmt.Println(len(ch))

ch <- 1
ch <- 2
ch <- 3

fmt.Println(len(ch))
}
  1. 通道的多路复用
    Go语言中提供了select关键字,可以同时响应多个通道的操作。select的每个case都会对应一个通道的收发过程。当收发完成时,就会触发case中响应的语句。多个操作在每次select中挑选一个进行响应。

select{
  case 操作1:
   响应操作1
  case 操作2:
   响应操作2
  defalut:
   没有操作情况
}

操作 语句实例
接收任意数据 case <-ch:
接收变量 case d := <-ch:
发送数据 case ch <- 100:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package main

import (
"errors"
"fmt"
"time"
)

func main() {
ch := make(chan string)

go RPCServer(ch)

recv, err := RPCClient(ch, "hi")
if err != nil {
fmt.Println(err)
} else {
fmt.Println("client received", recv)
}
}

func RPCClient(ch chan string, req string) (string, error) {
ch <- req

select {
case ack := <-ch:
return ack, nil
case <-time.After(time.Second):
return "", errors.New("Time out")
}
}

func RPCServer(ch chan string) {
for {
data := <-ch
fmt.Println("server received", data)

//模拟超时
//time.Sleep(time.Second * 2)

ch <- "roger"
}
}
  1. 同步

原子操作atmoic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
"fmt"
"sync"
"sync/atomic"
)

var seq int64
var seqGuard sync.Mutex

func GetId() int64 {
return atomic.AddInt64(&seq, 1)
}

func main() {
for i := 0; i < 10; i++ {
go GetId()
}

fmt.Println(GetId())
}

互斥锁
当对互斥量加锁后,另一个goroutine尝试继续加锁时将会发生阻塞,直到这个互斥量被解锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main

import (
"fmt"
"sync"
)

var (
count int
countGuard sync.Mutex
countRGuard sync.RWMutex
)

func GetCount() int {
countRGuard.Lock()
defer countRGuard.Unlock()

return count
}

func SetCount(c int) {
countGuard.Lock()
count = c
countGuard.Unlock()
}

func main() {
SetCount(1)

fmt.Println(GetCount())
}

等待组
等待组内部拥有一个计数器,计数器的值可以通过方法调用实现计数器的增减。等待组的计数器值为0时,表示所有任务已经完成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main

import (
"fmt"
"net/http"
"sync"
)

func main() {
var wg sync.WaitGroup

var urls = []string{
"http://www.github.com",
"thhps://www.baidu.com",
"thhps://www.google.com",
}

for _, url := range urls {
wg.Add(1)

go func(url string) {
defer wg.Done()

_, err := http.Get(url)
fmt.Println(url, err)
}(url)
}

wg.Wait()
fmt.Println("over")
}
Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2021 Azella
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信