跳到主要内容

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()
}