发布时间:2024-11-05 18:29:02
在Go语言的标准库中,sync包是一个非常重要的模块。它提供了一组用于多线程同步的基本接口和原语。在并发编程中,正确地处理并发访问共享资源是非常重要的。sync包通过提供互斥锁、条件变量等功能,帮助我们实现线程安全的并发程序。
互斥锁是最基本的同步工具之一,可以通过sync.Mutex类型对象实现。互斥锁可以通过Lock()方法获取,通过Unlock()方法释放。在任意时刻只能有一个goroutine持有互斥锁,其他goroutine必须等待互斥锁被释放才能继续执行。这样可以保证共享资源在任何时刻最多只能被一个goroutine访问,避免出现并发访问的问题。
以下是一个简单的示例代码:
import (
"fmt"
"sync"
)
func main() {
var mutex sync.Mutex
var data int
go func() {
mutex.Lock()
data = 100
mutex.Unlock()
}()
mutex.Lock()
fmt.Println(data)
mutex.Unlock()
}
在上面的代码中,我们使用mutex互斥锁来保护共享变量data的读写操作。在main函数中,第一个goroutine使用mutex.Lock()获取互斥锁后,将data设置为100,并通过mutex.Unlock()释放互斥锁。而在main函数中,则是使用mutex.Lock()获取互斥锁后,打印出data的值,并通过mutex.Unlock()释放互斥锁。这样就保证了共享变量data在任意时刻只有一个goroutine在访问。
除了互斥锁,sync包还提供了条件变量的实现,用于goroutine之间的等待和唤醒。条件变量可以通过sync.Cond类型对象实现。它与互斥锁配合使用,典型的使用场景是一个或多个goroutine需要等待某个条件满足后才能继续执行。
以下是一个简单的示例代码:
import (
"fmt"
"sync"
)
var cond = sync.NewCond(&sync.Mutex{})
var ready bool
func main() {
var wg sync.WaitGroup
wg.Add(1)
go func() {
cond.L.Lock()
for !ready {
cond.Wait()
}
fmt.Println("goroutine 1: ready")
cond.L.Unlock()
wg.Done()
}()
cond.L.Lock()
ready = true
cond.Signal()
cond.L.Unlock()
wg.Wait()
}
在上面的代码中,我们使用cond条件变量和互斥锁cond.L来实现一个等待/通知的模式。在main函数中,我们首先创建了一个WaitGroup对象wg,然后使用wg.Add(1)来增加等待的goroutine数量,以便在所有goroutine执行完毕后等待一段时间。接着我们使用cond.L.Lock()获取互斥锁,并设置ready变量为true,然后通过cond.Signal()方法发出信号唤醒正在等待的goroutine。最后使用完互斥锁后,通过cond.L.Unlock()释放互斥锁。
在另外一个goroutine中,我们使用cond.L.Lock()获取互斥锁,然后通过for循环等待条件ready为true。如果条件不满足,则通过cond.Wait()方法将goroutine阻塞并等待被唤醒。当收到cond.Signal()的通知后,goroutine被唤醒并打印出"goroutine 1: ready"。然后使用cond.L.Unlock()释放互斥锁,并通过wg.Done()告知主函数该goroutine已完成执行。
sync包还提供了读写锁sync.RWMutex,它比互斥锁更加灵活,允许多个goroutine同时持有读锁进行读操作,而只有一个goroutine持有写锁进行写操作。这样可以提高并发性能。
以下是一个简单的示例代码:
import (
"fmt"
"sync"
)
var (
data map[string]string
rwLock sync.RWMutex
)
func main() {
data = make(map[string]string)
go func() {
rwLock.Lock()
data["key"] = "value"
rwLock.Unlock()
}()
rwLock.RLock()
fmt.Println(data["key"])
rwLock.RUnlock()
}
在上面的代码中,我们使用rwLock读写锁来保护共享变量data的读写操作。在一个goroutine中,我们使用rwLock.Lock()获取写锁,然后向data中添加一个键值对,最后通过rwLock.Unlock()释放写锁。而在另外一个goroutine中,我们使用rwLock.RLock()获取读锁,并通过fmt.Println打印出data中指定键的值,然后通过rwLock.RUnlock()释放读锁。
通过使用读写锁,我们可以实现多个goroutine同时读取共享资源的操作,从而提高并发性能。