1.互斥锁
package main
import (
"fmt"
"sync"
)
var mutex sync.Mutex
var wg sync.WaitGroup
var x int = 0
func add() {
defer wg.Done()
mutex.Lock()
x++
mutex.Unlock()
}
func main() {
for i := 0; i < 1000; i++ {
wg.Add(1)
go add()
}
wg.Wait()
fmt.Println(x) //1000
}
2.读写互斥锁
场景一:写加互斥锁,读不加互斥锁
在新的数据产生之前会有脏数据,写的时候加锁,读的时候不加锁,程序运行过程中会读到脏数据,所以在读写临界资源的时候都需要加锁
package main
import (
"fmt"
"sync"
)
var count int = 0
var mutex sync.Mutex
var wg sync.WaitGroup
func write() {
defer wg.Done()
mutex.Lock()
tmp := count
count = -1 //脏数据
count = tmp
count++
mutex.Unlock()
}
func read() {
defer wg.Done()
if count == -1 {
fmt.Println("读到脏数据...")
}
}
func main() {
for i := 0; i < 100000; i++ {
wg.Add(2)
go write()
go read()
}
wg.Wait()
fmt.Println(count)
}
运行结果:
读到脏数据...
读到脏数据...
100000
场景二:写加互斥锁,读也加互斥锁
在新的数据产生之前会有脏数据,写的时候加锁,读的时候也加锁,程序运行过程中就不会读到脏数据
package main
import (
"fmt"
"sync"
"time"
)
var count int = 0
var mutex sync.RWMutex
var wg sync.WaitGroup
func write() {
defer wg.Done()
mutex.Lock()
tmp := count
count = -1 //脏数据
count = tmp
count++
mutex.Unlock()
}
func read() {
defer wg.Done()
mutex.Lock()
if count == -1 {
fmt.Println("读到脏数据...")
}
mutex.Unlock()
}
func main() {
start := time.Now()
for i := 0; i < 10000; i++ {
wg.Add(1)
go write()
}
for i := 0; i < 10000000; i++ {
wg.Add(1)
go read()
}
wg.Wait()
fmt.Println(count) //10000
fmt.Println(time.Now().Sub(start)) //4.9567829s
}
场景三:写加互斥锁,读加读锁
在场景二中虽然解决了读线程可能读到脏数据的问题,但是所有的读线程不能并行访问资源,同样的代码在读线程中使用互斥锁运行时长为 4.9567829s
,而将读线程中的互斥锁改为读锁运行时长为 1.6137083s
,可以提升程序运行效率
package main
import (
"fmt"
"sync"
"time"
)
var count int = 0
var mutex sync.RWMutex
var wg sync.WaitGroup
func write() {
defer wg.Done()
mutex.Lock()
tmp := count
count = -1 //脏数据
count = tmp
count++
mutex.Unlock()
}
func read() {
defer wg.Done()
mutex.RLock()
if count == -1 {
fmt.Println("读到脏数据...")
}
mutex.RUnlock()
}
func main() {
start := time.Now()
for i := 0; i < 10000; i++ {
wg.Add(1)
go write()
}
for i := 0; i < 10000000; i++ {
wg.Add(1)
go read()
}
wg.Wait()
fmt.Println(count) //10000
fmt.Println(time.Now().Sub(start)) //1.6137083s
}
3.sync.Once
确保程序只执行一次时使用,如下示例保证变量 count
只被加一次,不论有多少线程,输出结果都是1
package main
import (
"fmt"
"sync"
)
var count int = 0
var once sync.Once
var wg sync.WaitGroup
func write() {
defer wg.Done()
f := func() {
count++
}
once.Do(f)
}
func main() {
for i := 0; i < 10000; i++ {
wg.Add(1)
go write()
}
wg.Wait()
fmt.Println(count) //1
}
4.sync.Map
go语言内置的map不是并发安全的,提供一个并发安全版的sync.Map
package main
import (
"fmt"
"sync"
)
//内置操作方法Store、Load、LoadOrStore、Delete、Range
var sm = sync.Map{}
var wg sync.WaitGroup
var mutex sync.Mutex
func smRange(key interface{}, value interface{}) bool {
fmt.Printf("key:%v value:%v\n", key, value)
return true
}
func main() {
for i := 0; i < 20; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
sm.Store(i, i)
}(i)
}
wg.Wait()
sm.Range(smRange)
}
5.原子操作atomic
package main
import (
"fmt"
"sync"
"sync/atomic"
)
var count int64 = 0
var mutex sync.Mutex
var wg sync.WaitGroup
func write() {
defer wg.Done()
//count++
atomic.AddInt64(&count, 1)
}
func main() {
for i := 0; i < 100000; i++ {
wg.Add(1)
go write()
}
wg.Wait()
fmt.Println(count) //100000
}