您的位置:首页 > 教育 > 锐评 > googleseo推广_网站源码之家_杭州seo运营_引擎优化

googleseo推广_网站源码之家_杭州seo运营_引擎优化

2025/6/6 18:45:14 来源:https://blog.csdn.net/liuxiaojun828/article/details/146228281  浏览:    关键词:googleseo推广_网站源码之家_杭州seo运营_引擎优化
googleseo推广_网站源码之家_杭州seo运营_引擎优化

注意在通道中: // 尝试从ch1接收值 data, ok := <-ch1 // 判断通道的值的状态

 Go语言协程之间如何进行数据共享和同步?

在Go语言中,可以使用互斥锁(mutex)来实现协程之间的数据共享和同步。互斥锁是一种机制,用于保护共享资源的访问,防止多个协程同时修改数据。

要使用互斥锁,首先需要导入"sync"包,然后创建一个互斥锁对象:

import "sync"var mutex sync.Mutex

在需要修改共享资源的地方,可以使用互斥锁进行保护,例如:

mutex.Lock() // 加锁
// 修改共享资源的代码
mutex.Unlock() // 解锁

通过互斥锁的加锁和解锁操作,可以确保同一时间只有一个协程能够修改共享资源,从而实现数据的安全共享和同步。

总结:Go语言协程可以通过通道进行通信,通过互斥锁进行数据共享和同步。这些机制使得并发编程变得简单且高效,适用于各种并发场景。

互斥锁

互斥锁是一种常用的控制共享资源访问的方法,它能够保证同一时间只有一个 goroutine 可以访问共享资源。Go 语言中使用sync包中提供的Mutex类型来实现互斥锁。

sync.Mutex提供了两个方法供我们使用。

方法名功能
func (m *Mutex) Lock()获取互斥锁
func (m *Mutex) Unlock()释放互斥锁

我们在下面的示例代码中使用互斥锁限制每次只有一个 goroutine 才能修改全局变量x,从而修复上面代码中的问题。

package mainimport ("fmt""sync"
)// sync.Mutexvar (x int64wg sync.WaitGroup // 等待组m sync.Mutex // 互斥锁
)// add 对全局变量x执行5000次加1操作
func add() {for i := 0; i < 5000; i++ {m.Lock() // 修改x前加锁x = x + 1m.Unlock() // 改完解锁}wg.Done()
}func main() {wg.Add(2)go add()go add()wg.Wait()fmt.Println(x)
}

将上面的代码编译后多次执行,每一次都会得到预期中的结果——10000。

使用互斥锁能够保证同一时间有且只有一个 goroutine 进入临界区,其他的 goroutine 则在等待锁;当互斥锁释放后,等待的 goroutine 才可以获取锁进入临界区,多个 goroutine 同时等待一个锁时,唤醒的策略是随机的。

读写互斥锁

互斥锁是完全互斥的,但是实际上有很多场景是读多写少的,当我们并发的去读取一个资源而不涉及资源修改的时候是没有必要加互斥锁的,这种场景下使用读写锁是更好的一种选择。读写锁在 Go 语言中使用sync包中的RWMutex类型。

sync.RWMutex提供了以下5个方法。

方法名功能
func (rw *RWMutex) Lock()获取写锁
func (rw *RWMutex) Unlock()释放写锁
func (rw *RWMutex) RLock()获取读锁
func (rw *RWMutex) RUnlock()释放读锁
func (rw *RWMutex) RLocker() Locker返回一个实现Locker接口的读写锁

读写锁分为两种:读锁和写锁。当一个 goroutine 获取到读锁之后,其他的 goroutine 如果是获取读锁会继续获得锁,如果是获取写锁就会等待;而当一个 goroutine 获取写锁之后,其他的 goroutine 无论是获取读锁还是写锁都会等待。

下面我们使用代码构造一个读多写少的场景,然后分别使用互斥锁和读写锁查看它们的性能差异。

var (x       int64wg      sync.WaitGroupmutex   sync.Mutex  // 定义的互斥锁rwMutex sync.RWMutex  // 定义的是读写锁
)// writeWithLock 使用互斥锁的写操作
func writeWithLock() {mutex.Lock() // 加互斥锁x = x + 1time.Sleep(10 * time.Millisecond) // 假设读操作耗时10毫秒mutex.Unlock()                    // 解互斥锁wg.Done()
}// readWithLock 使用互斥锁的读操作
func readWithLock() {mutex.Lock()                 // 加互斥锁time.Sleep(time.Millisecond) // 假设读操作耗时1毫秒mutex.Unlock()               // 释放互斥锁wg.Done()
}// writeWithLock 使用读写互斥锁的写操作
func writeWithRWLock() {rwMutex.Lock() // 加写锁x = x + 1time.Sleep(10 * time.Millisecond) // 假设读操作耗时10毫秒rwMutex.Unlock()                  // 释放写锁wg.Done()
}// readWithRWLock 使用读写互斥锁的读操作
func readWithRWLock() {rwMutex.RLock()              // 加读锁time.Sleep(time.Millisecond) // 假设读操作耗时1毫秒rwMutex.RUnlock()            // 释放读锁wg.Done()
}func do(wf, rf func(), wc, rc int) {start := time.Now()// wc个并发写操作for i := 0; i < wc; i++ {wg.Add(1)go wf()}//  rc个并发读操作for i := 0; i < rc; i++ {wg.Add(1)go rf()}wg.Wait()cost := time.Since(start)fmt.Printf("x:%v cost:%v\n", x, cost)}

我们假设每一次读操作都会耗时1ms,而每一次写操作会耗时10ms,我们分别测试使用互斥锁和读写互斥锁执行10次并发写和1000次并发读的耗时数据。

// 使用互斥锁,10并发写,1000并发读
do(writeWithLock, readWithLock, 10, 1000) // x:10 cost:1.466500951s// 使用读写互斥锁,10并发写,1000并发读
do(writeWithRWLock, readWithRWLock, 10, 1000) // x:10 cost:117.207592ms

从最终的执行结果可以看出,使用读写互斥锁在读多写少的场景下能够极大地提高程序的性能。不过需要注意的是如果一个程序中的读操作和写操作数量级差别不大,那么读写互斥锁的优势就发挥不出来。

sync.WaitGroup

在代码中生硬的使用time.Sleep肯定是不合适的,Go语言中可以使用sync.WaitGroup来实现并发任务的同步。 sync.WaitGroup有以下几个方法:

方法名功能
func (wg * WaitGroup) Add(delta int)计数器+delta
(wg *WaitGroup) Done()计数器-1
(wg *WaitGroup) Wait()阻塞直到计数器变为0

sync.WaitGroup内部维护着一个计数器,计数器的值可以增加和减少。例如当我们启动了 N 个并发任务时,就将计数器值增加N。每个任务完成时通过调用 Done 方法将计数器减1。通过调用 Wait 来等待并发任务执行完,当计数器值为 0 时,表示所有并发任务已经完成。

我们利用sync.WaitGroup将上面的代码优化一下:

var wg sync.WaitGroupfunc hello() {defer wg.Done()fmt.Println("Hello Goroutine!")
}
func main() {wg.Add(1)go hello() // 启动另外一个goroutine去执行hello函数fmt.Println("main goroutine done!")wg.Wait()
}

需要注意sync.WaitGroup是一个结构体,进行参数传递的时候要传递指针。

sync.Map  // 解决 map 的并发性

Go 语言中内置的 map 不是并发安全的,请看下面这段示例代码。

package mainimport ("fmt""strconv""sync"
)var m = make(map[string]int)func get(key string) int {return m[key]
}func set(key string, value int) {m[key] = value
}func main() {wg := sync.WaitGroup{}for i := 0; i < 10; i++ {wg.Add(1)go func(n int) {key := strconv.Itoa(n)set(key, n)fmt.Printf("k=:%v,v:=%v\n", key, get(key))wg.Done()}(i)}wg.Wait()
}

将上面的代码编译后执行,会报出fatal error: concurrent map writes错误。我们不能在多个 goroutine 中并发对内置的 map 进行读写操作,否则会存在数据竞争问题。

像这种场景下就需要为 map 加锁来保证并发的安全性了,Go语言的sync包中提供了一个开箱即用的并发安全版 map——sync.Map。开箱即用表示其不用像内置的 map 一样使用 make 函数初始化就能直接使用。同时sync.Map内置了诸如StoreLoadLoadOrStoreDeleteRange等操作方法。

方法名功能
func (m *Map) Store(key, value interface{})存储key-value数据
func (m *Map) Load(key interface{}) (value interface{}, ok bool)查询key对应的value
func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)查询或存储key对应的value
func (m *Map) LoadAndDelete(key interface{}) (value interface{}, loaded bool)查询并删除key
func (m *Map) Delete(key interface{})删除key
func (m *Map) Range(f func(key, value interface{}) bool)对map中的每个key-value依次调用f

下面的代码示例演示了并发读写sync.Map

package mainimport ("fmt""strconv""sync"
)// 并发安全的map
var m = sync.Map{}func main() {wg := sync.WaitGroup{}// 对m执行20个并发的读写操作for i := 0; i < 20; i++ {wg.Add(1)go func(n int) {key := strconv.Itoa(n)m.Store(key, n)         // 存储key-valuevalue, _ := m.Load(key) // 根据key取值fmt.Printf("k=:%v,v:=%v\n", key, value)wg.Done()}(i)}wg.Wait()
}

sync.once  操作  once.Do //  只关闭一次

// 定义一个匿名函数

synce .once  的使用方法:在头部声明:once  sync.Once

//这里是 main 函数里面的信息:// make channel 的应用ch1 := make(chan int, 10)ch2 := make(chan int, 10)wg.Add(3)go f1(ch1)go f2(ch1, ch2)go f2(ch1, ch2)wg.Wait()for ret := range ch2 {fmt.Println(ret)}///// 把数据 存入到 ch1 的通道里面去
func f1(ch chan int) {defer wg.Done()for i := 0; i < 10; i++ {ch <- i}close(ch)
}// 把 ch1 的结果 乘以 2 再存入到 ch2 中去
func f2(ch1 chan int, ch2 chan int) {defer wg.Done()// 这里是通过循环 赋值的一种方法//for v := range ch1 {//	ch2 <- v * 2//}// 这个是第二种方法for {val, ok := <-ch1if !ok {break}ch2 <- val * 2}//close(ch2)//f := func() { close(ch2) }//once := sync.Once{func() { close(ch2) }}once.Do(func() {close(ch2)})
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com