返回

探索 Go 协程间的通信:利用共享变量

后端

在 Go 中,协程是轻量级线程,可以同时执行多个任务。协程间通信是并发编程中的一个关键方面。在本文中,我们将探索利用共享变量实现协程间通信的方法。

共享变量

协程可以通过共享变量进行通信。当一个协程修改共享变量时,其他协程会看到这些修改。但是,共享变量需要小心使用,因为多个协程同时访问共享变量可能会导致数据竞争。

数据互斥

为了防止数据竞争,我们需要确保只有一个协程在给定的时间点访问共享变量。我们可以通过互斥锁或通道来实现数据互斥。

互斥锁

互斥锁是一种机制,它允许一次只有一个协程访问共享变量。当一个协程获取互斥锁时,其他协程将被阻止,直到该协程释放互斥锁。

import (
	"sync"
	"time"
)

var (
	counter int
	mu      sync.Mutex
)

func main() {
	for i := 0; i < 10; i++ {
		go func(i int) {
			mu.Lock()
			counter++
			time.Sleep(time.Second)
			mu.Unlock()
		}(i)
	}

	time.Sleep(11 * time.Second)
	fmt.Println(counter) // 输出:10
}

通道

通道是另一种实现数据互斥的方法。通道是一个 FIFO 队列,协程可以通过发送或接收数据进行通信。当一个协程发送数据时,它将被放入通道中。当另一个协程接收数据时,它将从通道中取出数据。

import (
	"sync"
)

var (
	counter int
	ch      = make(chan struct{})
	mu      sync.Mutex
)

func main() {
	for i := 0; i < 10; i++ {
		go func(i int) {
			mu.Lock()
			ch <- struct{}{}
			mu.Unlock()
			counter++
			<-ch
		}(i)
	}

	time.Sleep(11 * time.Second)
	fmt.Println(counter) // 输出:10
}

避免循环依赖

当多个协程通过共享变量进行通信时,可能会出现循环依赖。例如,如果协程 A 发送数据给协程 B,而协程 B 依赖于协程 A 完成某些操作,则可能会导致死锁。

为了避免循环依赖,我们应该设计我们的程序,以便协程之间的通信呈单向流动。例如,协程 A 可以向协程 B 发送数据,但协程 B 不应向协程 A 发送数据。

结论

在 Go 中,利用共享变量实现协程间通信是一种简单而有效的技术。但是,重要的是要小心处理数据竞争,并避免循环依赖。通过正确使用互斥锁或通道,我们可以确保协程间通信安全且高效。