Golang -- goroutine实例(乒乓球双打比赛) - Go语言中文社区

Golang -- goroutine实例(乒乓球双打比赛)


题目:

  1. 有四个选手, A1和A2为一个队, B1和B2为另一个队. A1首先发球(启动球), 然后B1, A2, B2将最后发球. 每一轮每个选手发2个球.
  2. 选手不改变他们的位置.
  3. 比赛期间, 双方选手必须轮流发球,并且在同一个队伍的两个选手可以竞争发球.
  4. 当轮到某个选手时, 他/她可以调用一个叫做shot(rate) 的随机函数来模拟比赛,在给定概率rate以内,该函数返回 “in”, 否则返回”out”. 例如 rate=85%, 则球在界内的概率为85%, 出界的概率为15%.
  5. 如果shot函数返回”in”, 对方选手必须调用shot函数把球打回.
  6. 如果shot函数返回”out”, 对方选手赢得1分,随后重新发球.
  7. 当每个选手发完2个球后比赛终止.分数多的一方赢得比赛.分数一样多,比赛为平局.
  8. 每个选手作为一个线程实现.

实现思路:

这里写图片描述
serve: 是指发球的goroutine.
serveMetux: 发球锁。
playerA: 是指A队伍抢到球的goroutine.
playerB: 是指B队伍抢到球的goroutine.
catch_chanel_A : 给A队伍的球的通道。
catch_chanel_B : 给B队伍的球的通道。

大致步骤:
1. serve 先加锁,后发球(将TableTennis放入通道中,如catch_chanel_A ),然后重复上锁(serveMetux)阻塞自身。
2. playerA 如果catch_chanel_A 没有球,阻塞自身。如果有球,则从通道中拿到球,shot(rate) 后返回in, 记录信息后,则将球放入给B的通道catch_chanel_B中。
3. playerB 如果catch_chanel_B 没有球,阻塞自身。如果有球,则从通道中拿到球,shot(rate) 后返回in, 记录信息后,则将球放入给B的通道catch_chanel_A中。如果shot(rate)返回out, 则解除锁(serveMetux),此时serve唤醒,开始下一次发球。

实现代码:

package main

import (
    "fmt"
    "sync"
    "math/rand"
    "container/ring"
    "strings"
    "time"
)

var (
    wg             sync.WaitGroup // 用于goroutine计数
    times          = 2  // 每个选手发球次数
    nums           = 4  // 多少个选手
    serveTotals    = nums * times  // 总发球次数
    score_balls_A  = make([]TableTennis, 0, serveTotals) // A的得分球
    score_balls_B  = make([]TableTennis, 0, serveTotals) // B的得分球
    turn           = ring.New(4)                         // 发球顺序
    serveMetux     sync.Mutex                            // 发球锁
    catch_chanel_B = make(chan TableTennis, 0)           // B队伍接球的通道
    catch_chanel_A = make(chan TableTennis, 0)           // A队伍接球的通道
    balls_ids      = make(chan int, serveTotals)         // 球的id
)

// 乒乓球
type TableTennis struct {
    id    int
    trail string // 球的轨迹
}

func serve() {
    defer wg.Done()

    // 初始化发球顺序
    turn.Value = "A1"
    turn = turn.Next()
    turn.Value = "B1"
    turn = turn.Next()
    turn.Value = "A2"
    turn = turn.Next()
    turn.Value = "B2"

    // 开始发球
    for i := 0; i < times; i++ {
        for j := 0; j < nums; j++ {
            serveMetux.Lock() // 解锁时发下一个球

            turn = turn.Next()
            name := turn.Value.(string)
            t := TableTennis{<-balls_ids, name + "-in"}

            if name[0] == 'A' {
                catch_chanel_B <- t
            } else {
                catch_chanel_A <- t
            }
        }
    }
    time.Sleep(time.Second)  // 等待player goroutine对catch_chanel的使用
    close(catch_chanel_A)
    close(catch_chanel_B)
}

// A队选手
func playerA(name string, rate int) {
    defer wg.Done() // 延迟递减计数

    for t := range catch_chanel_A {
        // 2. 将球击打出去
        rest := shot(rate)
        // 3. 记录球的轨迹
        t.trail += "-" + name + "-" + rest
        // 球出界
        if strings.Compare("out", rest) == 0 {
            // 对方得分
            score_balls_B = append(score_balls_B, t)
            fmt.Println(t)
            serveMetux.Unlock()
            continue
        }
        // 4. 对面队伍准备接球

        catch_chanel_B <- t
    }

}

// B队选手
func playerB(name string, rate int) {
    defer wg.Done() // 延迟递减计数
    for t := range catch_chanel_B {
        // 2. 将球击打出去
        rest := shot(rate)
        // 3. 记录球的轨迹
        t.trail += "-" + name + "-" + rest
        // 球出界
        if strings.Compare("out", rest) == 0 {
            // 对方得分
            score_balls_A = append(score_balls_A, t)
            fmt.Println(t)
            serveMetux.Unlock()
            continue
        }
        // 4. 对面队伍准备接球
        catch_chanel_A <- t
    }
}

// 击球
func shot(rate int) string {
    if rand.Intn(100) < rate {
        return "in"
    } else {
        return "out"
    }
}

func main() {
    fmt.Println("比赛开始...")

    // 初始化球的id
    for i := 0; i < serveTotals; i++ {
        balls_ids <- i + 1
    }

    // 初始化发球顺序

    wg.Add(nums + 1) // 累加计数
    go serve()
    //time.Sleep(time.Second)

    go playerA("A1", 45)
    go playerA("A2", 60)

    go playerB("B1", 50)
    go playerB("B2", 90)

    wg.Wait()

    fmt.Println("比赛结束.")
    fmt.Printf("A : B = (%d, %d)n", len(score_balls_A), len(score_balls_B))

    for _, t := range score_balls_A {
        fmt.Println(t)
    }
    fmt.Println()

    for _, t := range score_balls_B {
        fmt.Println(t)
    }

}

结果:

这里写图片描述

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_27068845/article/details/72857561
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2019-09-14 00:10:31
  • 阅读 ( 1359 )
  • 分类:Go

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢