Go 语言
代码输出
Mutex
main.go
package main
import (
"fmt"
"sync"
)
var mu sync.Mutex
var chain string
func main() {
chain = "main"
A()
fmt.Println(chain)
}
func A() {
mu.Lock()
defer mu.Unlock()
chain = chain + " --> A"
B()
}
func B() {
chain = chain + " --> B"
C()
}
func C() {
mu.Lock()
defer mu.Unlock()
chain = chain + " --> C"
}
输出:
fatal error: all goroutines are asleep - deadlock!
这个代码发生死锁的原因是:
- A 函数在调用 B 函数时持有锁 mu。
- B 函数调用 C 函数。
- C 函数尝试获取同一个锁 mu,但由于 A 函数还没有释放锁 mu,所以 C 函数会被阻塞。
导致死锁的具体原因是函数 A 在调用 B 时持有锁,B 再调用 C,而 C 又尝试获取同一个锁。
RWMutex
package main
import (
"fmt"
"sync"
"time"
)
var mu sync.RWMutex
var count int
func main() {
go A()
time.Sleep(2 * time.Second)
mu.Lock()
defer mu.Unlock()
count++
fmt.Println(count)
}
func A() {
mu.RLock()
defer mu.RUnlock()
B()
}
func B() {
time.Sleep(5 * time.Second)
C()
}
func C() {
mu.RLock()
defer mu.RUnlock()
}
输出:
fatal error: all goroutines are asleep - deadlock!
这个代码发生死锁的原因是:
- main 函数启动一个 goroutine 执行 A 函数。
- A 函数获取了读锁 mu.RLock(),然后调用 B 函数。
- B 函数调用 C 函数,C 函数再次获取了读锁 mu.RLock()。
- 同时,main 函数等待 2 秒钟,然后尝试获取写锁 mu.Lock()。
问题在于,当一个 goroutine 持有读锁时,其他 goroutine 仍然可以获取读锁,但不能获取写锁。写锁会等待所有的读锁释放后才能获得。因此,main 函数在等待写锁,而 A 函数及其调用链中的 B 和 C 函数持有读锁并且不会释放,导致所有的 goroutine 都在等待,产生死锁。
Channel
package main
import "fmt"
func main() {
var ch chan int
var count int
go func() {
ch <- 1
}()
go func() {
count++
close(ch)
}()
<-ch
fmt.Println(count)
}
输出:
panic: close of nil channel
原因:ch 未有被初始化,关闭时会报错。
代码编写
打印出现次数最多的字符
func main() {
input := "example string with some repeated characters"
m := make(map[rune]int)
for _, v := range input {
m[v]++
}
var maxChar rune
var maxCount int
for k, v := range m {
if v > maxCount {
maxCount = v
maxChar = k
}
}
fmt.Printf("出现最多的字符是 '%c',出现了 %d 次\n", maxChar, maxCount)
}
sync.WaitGroup 中 Wait 函数支持超时功能
func WaitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
ch := make(chan bool, 1)
go time.AfterFunc(timeout, func() {
ch <- true
})
go func() {
wg.Wait()
ch <- false
}()
return <-ch
}
翻转字符串
src/slices/slices.go
func Reverse[S ~[]E, E any](s S) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
}
交替打印字母和数字
使用两个 goroutine 交替打印序列,一个 goroutine 打印数字, 另外一个 goroutine 打印字母。效果如下:
12AB34CD56EF78GH910IJ1112KL1314MN1516OP1718QR1920ST2122UV2324WX2526YZ2728
实现:
package main
import "fmt"
func main() {
number := make(chan bool)
letter := make(chan bool)
done := make(chan bool)
go func() {
i := 1
for {
select {
case <-number:
fmt.Print(i)
i++
fmt.Print(i)
i++
letter <- true
}
}
}()
go func() {
j := 'A'
for {
select {
case <-letter:
if j >= 'Z' {
done <- true
} else {
fmt.Print(string(j))
j++
fmt.Print(string(j))
j++
number <- true
}
}
}
}()
number <- true
for {
select {
case <-done:
return
}
}
}
golang 协程和 channel 配合使用
写代码实现两个 goroutine,其中一个产生随机数并写入到 go channel 中,另外一个从 channel 中读取数字并打印到标准输出。最终输出五个随机数。
实现:
package main
import (
"fmt"
"math/rand"
"sync"
)
func main() {
out := make(chan int)
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
for i := 0; i < 5; i++ {
out <- rand.Intn(5)
}
close(out)
}()
go func() {
defer wg.Done()
for i := range out {
fmt.Println(i)
}
}()
wg.Wait()
}