线程同步类
此类题目一般是指定线程的输出顺序,或者是不同线程之间存在依赖关系。
1.要求线程a执行完才开始线程b, 线程b执行完才开始线程
package main
import (
"fmt"
"sync"
)
//有明确的前后阻塞条件
func print(nums []int, preChan chan struct{}, postChan chan struct{}, wg *sync.WaitGroup) {
<-preChan
for _, num := range nums {
fmt.Println(num)
}
postChan <- struct {
}{}
wg.Done()
}
func main() {
ch1 := make(chan struct{}, 1)
ch2 := make(chan struct{}, 1)
wg := &sync.WaitGroup{}
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
wg.Add(2)
go print(nums[:2], ch2, ch1, wg)
go print(nums[2:], ch1, ch2, wg)
ch2 <- struct{}{}
wg.Wait()
close(ch1)
close(ch2)
fmt.Println("done")
}
2.两个线程轮流打印数字,一直到100
package main
import (
"fmt"
"sync"
)
func print(preChan chan int, postChan chan int, wg *sync.WaitGroup) {
for {
select {
case i := <-preChan:
if i > 100 {
wg.Done()
postChan <- i + 1
return
}
fmt.Println(i)
postChan <- i + 1
}
}
}
func main() {
ch1 := make(chan int, 1)
ch2 := make(chan int, 1)
wg := &sync.WaitGroup{}
wg.Add(2)
go print(ch2, ch1, wg)
go print(ch1, ch2, wg)
ch2 <- 1
wg.Wait()
close(ch1)
close(ch2)
fmt.Println("done")
}
3.编写一个程序,启动三个线程,三个线程的ID分别是A,B,C;,每个线程将自己的ID值在屏幕上打印5遍,打印顺序是ABCABC…
package main
import (
"fmt"
"sync"
)
func print(id string, preChan <-chan struct{}, nextChan chan<- struct{}, wg *sync.WaitGroup) {
for i := 0; i < 5; i++ {
<-preChan
fmt.Print(id)
nextChan <- struct{}{}
}
wg.Done()
}
func main() {
wg := &sync.WaitGroup{}
wg.Add(3)
ch1 := make(chan struct{}, 1)
ch2 := make(chan struct{}, 1)
ch3 := make(chan struct{}, 1)
go print("A", ch1, ch2, wg)
go print("B", ch2, ch3, wg)
go print("C", ch3, ch1, wg)
ch1 <- struct{}{}
wg.Wait()
}
并发数据同步类
此类型一般是在多并发情景下保证数据的一致性。
1.编写10个线程,第一个线程从1加到10,第二个线程从11加20…第十个线程从91加到100,最后再把10个线程结果相加。
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func Sum(nums []int, ans *atomic.Int32) {
tmp := 0
for _, v := range nums {
tmp += v
}
ans.Add(int32(tmp))
}
func main() {
wg := &sync.WaitGroup{}
ans := &atomic.Int32{}
wg.Add(10)
for i := 0; i <= 9; i++ {
go func(n int, ans *atomic.Int32) {
defer wg.Done()
nums := make([]int, 0, 10)
for i := 1; i <= 10; i++ {
nums = append(nums, i+n*10)
}
Sum(nums, ans)
}(i, ans)
}
wg.Wait()
fmt.Println(ans.Load())
}
2.三个窗口同时卖票
由于此处的场景比较简单,仅需要更新余票数目,因此我们使用 atomic 会更加高效。
而在需要维护的数据较为复杂时,更应该使用 mutex,atomic 适合维护的数据是整数或是布尔值的情况。
使用 cond 来进行同步启动
package main
import (
"fmt"
"math/rand"
"sync"
"sync/atomic"
"time"
)
func Sale(total *atomic.Int64, ID string, wg *sync.WaitGroup, start *sync.Cond, mutex *sync.Mutex, ready *bool) {
start.L.Lock()
for !*ready {
start.Wait()
}
start.L.Unlock()
for {
mutex.Lock()
if total.Load() > 0 {
total.Add(-1)
fmt.Println(ID + " buy a ticket")
mutex.Unlock()
} else {
mutex.Unlock()
break
}
time.Sleep(time.Millisecond*time.Duration(rand.Intn(500)) + time.Millisecond*500)
}
wg.Done()
}
func main() {
total := atomic.Int64{}
total.Store(100)
wg := sync.WaitGroup{}
wg.Add(3)
cond := sync.NewCond(&sync.Mutex{})
mutex := sync.Mutex{}
ready := false
go Sale(&total, "1", &wg, cond, &mutex, &ready)
go Sale(&total, "2", &wg, cond, &mutex, &ready)
go Sale(&total, "3", &wg, cond, &mutex, &ready)
ready = true
cond.Broadcast()
wg.Wait()
}
生产者消费者
模拟化学反应 $2H_2 + O_2 = 2H_2O$
package main
import (
"fmt"
"sync"
"sync/atomic"
"time"
)
var (
h2Count atomic.Int32
o2Count atomic.Int32
fireCond = sync.NewCond(&sync.Mutex{})
H2Cond = sync.NewCond(&sync.Mutex{})
O2Cond = sync.NewCond(&sync.Mutex{})
)
func react(id string) {
for {
// 1. 生成需要两份的氢气
H2Cond.L.Lock()
if h2Count.Load() < 2 {
H2Cond.Wait()
}
h2Count.Add(-2)
H2Cond.L.Unlock()
// 2. 生成需要一份的氧气
O2Cond.L.Lock()
if o2Count.Load() < 1 {
O2Cond.Wait()
}
o2Count.Add(-1)
O2Cond.L.Unlock()
// 3. 生成水
fireCond.L.Lock()
fireCond.Wait()
fireCond.L.Unlock()
fmt.Println(id + "生成水")
}
}
func produceHydrogen() {
ticker := time.NewTicker(1 * time.Second)
for {
select {
case <-ticker.C:
h2Count.Add(1)
if h2Count.Load() >= 2 {
H2Cond.Signal()
}
}
}
}
func produceOxygen() {
ticker := time.NewTicker(1 * time.Second)
for {
select {
case <-ticker.C:
o2Count.Add(1)
O2Cond.Signal()
}
}
}
func produceFire() {
ticker := time.NewTicker(1 * time.Second)
for {
select {
case <-ticker.C:
fireCond.Broadcast()
}
}
}
func main() {
go produceOxygen()
go produceHydrogen()
go produceHydrogen()
go produceHydrogen()
go produceFire()
go react("1")
go react("2")
time.Sleep(1 * time.Minute)
}