Go语言多线程问题

发布于:2025-06-10 ⋅ 阅读:(19) ⋅ 点赞:(0)

打印零与奇偶数(leetcode 1116)

方法1:使用互斥锁和条件变量

package main

import (
	"fmt"
	"sync"
)

type ZeroEvenOdd struct {
	n         int
	zeroMutex sync.Mutex
	evenMutex sync.Mutex
	oddMutex  sync.Mutex
	current   int
}

func NewZeroEvenOdd(n int) *ZeroEvenOdd {
	z := &ZeroEvenOdd{n: n}
	z.evenMutex.Lock()
	z.oddMutex.Lock()
	return z
}

func (z *ZeroEvenOdd) Zero(printNumber func(int)) {
	for i := 0; i < z.n; i++ {
		z.zeroMutex.Lock()
		printNumber(0)
		z.current++
		if z.current%2 == 1 {
			z.oddMutex.Unlock()
		} else {
			z.evenMutex.Unlock()
		}
	}
}

func (z *ZeroEvenOdd) Even(printNumber func(int)) {
	for i := 2; i <= z.n; i += 2 {
		z.evenMutex.Lock()
		printNumber(i)
		z.zeroMutex.Unlock()
	}
}

func (z *ZeroEvenOdd) Odd(printNumber func(int)) {
	for i := 1; i <= z.n; i += 2 {
		z.oddMutex.Lock()
		printNumber(i)
		z.zeroMutex.Unlock()
	}
}

func main() {
	n := 5
	zeo := NewZeroEvenOdd(n)

	var wg sync.WaitGroup
	wg.Add(3)

	printFunc := func(x int) {
		fmt.Print(x)
	}

	go func() {
		defer wg.Done()
		zeo.Zero(printFunc)
	}()

	go func() {
		defer wg.Done()
		zeo.Even(printFunc)
	}()

	go func() {
		defer wg.Done()
		zeo.Odd(printFunc)
	}()

	wg.Wait()
	fmt.Println()
}

方法2:使用通道同步

package main

import (
	"fmt"
	"sync"
)

type ZeroEvenOdd struct {
	n       int
	zeroCh  chan struct{}
	evenCh  chan struct{}
	oddCh   chan struct{}
	done    chan struct{}
}

func NewZeroEvenOdd(n int) *ZeroEvenOdd {
	z := &ZeroEvenOdd{
		n:      n,
		zeroCh: make(chan struct{}),
		evenCh: make(chan struct{}),
		oddCh:  make(chan struct{}),
		done:   make(chan struct{}),
	}
	close(z.zeroCh) // 初始允许zero执行
	return z
}

func (z *ZeroEvenOdd) Zero(printNumber func(int)) {
	for i := 0; i < z.n; i++ {
		<-z.zeroCh
		printNumber(0)
		if i%2 == 0 {
			z.oddCh <- struct{}{}
		} else {
			z.evenCh <- struct{}{}
		}
	}
	close(z.done)
}

func (z *ZeroEvenOdd) Even(printNumber func(int)) {
	for i := 2; i <= z.n; i += 2 {
		<-z.evenCh
		printNumber(i)
		z.zeroCh <- struct{}{}
	}
}

func (z *ZeroEvenOdd) Odd(printNumber func(int)) {
	for i := 1; i <= z.n; i += 2 {
		<-z.oddCh
		printNumber(i)
		z.zeroCh <- struct{}{}
	}
}

func main() {
	n := 5
	zeo := NewZeroEvenOdd(n)

	var wg sync.WaitGroup
	wg.Add(3)

	printFunc := func(x int) {
		fmt.Print(x)
	}

	go func() {
		defer wg.Done()
		zeo.Zero(printFunc)
	}()

	go func() {
		defer wg.Done()
		zeo.Even(printFunc)
	}()

	go func() {
		defer wg.Done()
		zeo.Odd(printFunc)
	}()

	wg.Wait()
	fmt.Println()
}

方法3:使用原子计数器

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

type ZeroEvenOdd struct {
	n       int
	current int32
	cond    *sync.Cond
}

func NewZeroEvenOdd(n int) *ZeroEvenOdd {
	return &ZeroEvenOdd{
		n:       n,
		current: 0,
		cond:    sync.NewCond(&sync.Mutex{}),
	}
}

func (z *ZeroEvenOdd) Zero(printNumber func(int)) {
	for i := 0; i < z.n; i++ {
		z.cond.L.Lock()
		for atomic.LoadInt32(&z.current) != 0 {
			z.cond.Wait()
		}
		printNumber(0)
		atomic.StoreInt32(&z.current, int32(i+1))
		z.cond.Broadcast()
		z.cond.L.Unlock()
	}
}

func (z *ZeroEvenOdd) Even(printNumber func(int)) {
	for i := 2; i <= z.n; i += 2 {
		z.cond.L.Lock()
		for atomic.LoadInt32(&z.current) != int32(i) {
			z.cond.Wait()
		}
		printNumber(i)
		atomic.StoreInt32(&z.current, 0)
		z.cond.Broadcast()
		z.cond.L.Unlock()
	}
}

func (z *ZeroEvenOdd) Odd(printNumber func(int)) {
	for i := 1; i <= z.n; i += 2 {
		z.cond.L.Lock()
		for atomic.LoadInt32(&z.current) != int32(i) {
			z.cond.Wait()
		}
		printNumber(i)
		atomic.StoreInt32(&z.current, 0)
		z.cond.Broadcast()
		z.cond.L.Unlock()
	}
}

func main() {
	n := 5
	zeo := NewZeroEvenOdd(n)

	var wg sync.WaitGroup
	wg.Add(3)

	printFunc := func(x int) {
		fmt.Print(x)
	}

	go func() {
		defer wg.Done()
		zeo.Zero(printFunc)
	}()

	go func() {
		defer wg.Done()
		zeo.Even(printFunc)
	}()

	go func() {
		defer wg.Done()
		zeo.Odd(printFunc)
	}()

	wg.Wait()
	fmt.Println()
}

哲学家进食

package main

import (
	"fmt"
	"sync"
	"time"
)

const numPhilosophers = 5

type Philosopher struct {
	id          int
	leftFork    *sync.Mutex
	rightFork   *sync.Mutex
	eatingTimes int
}

func (p *Philosopher) think() {
	fmt.Printf("哲学家 %d 正在思考...\n", p.id)
	time.Sleep(time.Second * 1)
}

func (p *Philosopher) eat() {
	// 确定拿叉子的顺序(避免循环等待)
	first, second := p.leftFork, p.rightFork
	if p.id%2 == 0 { // 偶数ID哲学家先拿右叉子
		first, second = p.rightFork, p.leftFork
	}

	// 获取叉子
	first.Lock()
	second.Lock()

	// 进餐
	fmt.Printf("哲学家 %d 开始进餐 (第%d次)\n", p.id, p.eatingTimes+1)
	time.Sleep(time.Second * 1)
	p.eatingTimes++

	// 释放叉子
	second.Unlock()
	first.Unlock()
}

func (p *Philosopher) dine(sem chan struct{}, wg *sync.WaitGroup) {
	defer wg.Done()
	
	for i := 0; i < 3; i++ { // 每个哲学家吃3次便于观察
		p.think()
		
		// 获取就餐许可
		sem <- struct{}{}
		p.eat()
		<-sem
	}
}

func main() {
	// 初始化叉子
	forks := make([]*sync.Mutex, numPhilosophers)
	for i := range forks {
		forks[i] = &sync.Mutex{}
	}

	// 创建哲学家
	philosophers := make([]*Philosopher, numPhilosophers)
	for i := range philosophers {
		philosophers[i] = &Philosopher{
			id:        i,
			leftFork:  forks[i],
			rightFork: forks[(i+1)%numPhilosophers],
		}
	}

	// 使用信号量限制同时就餐人数(最多允许4人同时尝试拿叉子)
	sem := make(chan struct{}, numPhilosophers-1)
	var wg sync.WaitGroup

	// 开始就餐
	for _, p := range philosophers {
		wg.Add(1)
		go p.dine(sem, &wg)
	}

	wg.Wait()
	
	// 统计每位哲学家就餐次数
	for _, p := range philosophers {
		fmt.Printf("哲学家 %d 总共进餐 %d 次\n", p.id, p.eatingTimes)
	}
}