CSP(Communication Sequential Process)
提倡通过通信共享内存,而不是通过共享内存实现通信
Channel 应用
以下代码展示了使用goroutine和通道来实现一个简单的平方计算过程
func calSquare() {
src := make(chan int)
dest := make(chan []int, 3)
A := func() {
defer close(src)
for i := 0; i < 10; i++ {
src <- i
}
}
B := func() {
defer close(dest)
for i := range src {
dest <- []int{i, i * i}
}
}
go A()
go B()
for i := range dest {
fmt.Println(i[0], "的平方是", i[1])
}
}
func main() {
calSquare()
}
输出结果
0 的平方是 0
1 的平方是 1
2 的平方是 4
3 的平方是 9
4 的平方是 16
5 的平方是 25
6 的平方是 36
7 的平方是 49
8 的平方是 64
9 的平方是 81
这个例子展示了如何使用goroutine和通道来实现并发计算,并在并发的计算结果中进行同步
这种机制允许我们在并发场景下实现更高效和灵活的计算过程
Lock 应用
以下代码涉及了并发场景下对共享变量的操作,并使用了互斥锁(sync.Mutex)来保护共享变量
var (
// 共享变量
x int64
// 互斥锁
lock sync.Mutex
)
func addWithoutLock() {
for i := 0; i < 2000; i++ {
x += 1
}
}
func addWithLock() {
for i := 0; i < 2000; i++ {
lock.Lock()
// 临界区
x += 1
// 临界区
lock.Unlock()
}
}
func add() {
x = 0
for i := 0; i < 5; i++ {
go addWithoutLock()
}
time.Sleep(time.Second)
fmt.Println("without lock:", x) // 没有加锁保护,值不确定
x = 0
for i := 0; i < 5; i++ {
go addWithLock()
}
time.Sleep(time.Second)
fmt.Println("with lock:", x) // 每次只有一个goroutine可以修改x的值
}
func main() {
add()
}
输出结果
without lock: 9127 // 没有锁保护,值异常
with lock: 10000
在实际开发中,对共享变量的访问需要谨慎处理,特别是在并发环境下
使用互斥锁来保护共享变量是一种常见的方式,但也需要注意避免死锁和过多的锁竞争,以提高程序的性能和可维护性
WaitGroup 应用
以下代码展示了使用 goroutine 和 sync.WaitGroup 来创建多个并发的打印操作
func hello(i int) {
fmt.Println("hello", i)
}
func helloGoroutine() {
n := 5
var wg sync.WaitGroup
wg.Add(n)
for i := 0; i < n; i++ {
go func(j int) {
defer wg.Done()
hello(j) // goroutine 并发执行,打印的结果随机
}(i)
}
wg.Wait() // wg 将在 n 个 goroutine 完成之后解除阻塞
}
func main() {
helloGoroutine()
}
输出结果
hello 0
hello 4
hello 1
hello 2
hello 3
执行wg.Done()后,WaitGroup 中的计数器 -1
如果在计数器为 0 时调用wg.Done(),会引发 panic,因为计数器的值不能为负
如果没有wg.Wait()的等待操作,程序将在创建了 n 个 gocroutine 后直接结束,没有机会输出结果
WaitGroup 允许我们在并发场景下有效地控制和同步多个 goroutine 的执行,以实现更高效和并发的程序