热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

题目:有四个选手,A1和A2为一个队,B1和B2为另一个队.A1首先发球(启动球),然后B1,A2,B2将最后发球.每一轮每个选手发2个球.选手不改变他们的位置.比赛期

题目:
  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 for j := 0; 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) return "in"
} else {
return "out"
}
}

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

// 初始化球的id
for i := 0; 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)
}

}

结果:

这里写图片描述


推荐阅读
  • Go 快速入门指南命令行参数
    命令行参数个数调用os包即可。获取参数个数,遍历参数packagemainimport(fmtos)funcmain(){fmt.Printf(Numberofargsi ... [详细]
  • 目录在Go语言项目中使用Zap日志库介绍默认的GoLogger日志库实现GoLogger设置Logger使用LoggerLogger的运行GoLogger的优势和劣势优势劣势Ube ... [详细]
  • golang 解析磁力链为 torrent 相关的信息
    其实通过http请求已经获得了种子的信息了,但是传播存储种子好像是违法的,所以就存储些描述信息吧。之前python跑的太慢了。这个go并发不知道写的有没有问题?!packag ... [详细]
  • 集成第三方库,自检测读取配置文件。文件读取,结构体定义,接口实现,错误返回,库解析,适合新同学练手。思路文件读取获取字节流文件类型分析,确定解析api集成第三方解析api管理器定义 ... [详细]
  • 小编给大家分享一下Golang端口复用测试的实现方法,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有 ... [详细]
  • 20220811:以下go语言代码输出什么?A:panic;B:编译错误;C:json marshal 报错
    2022-08-11:以下go语言代码输出什么?A:panic;B:编译错误;C:jsonmarshal报错;D:null;E:nil。packagemainimport(enc ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • (三)多表代码生成的实现方法
    本文介绍了一种实现多表代码生成的方法,使用了java代码和org.jeecg框架中的相关类和接口。通过设置主表配置,可以生成父子表的数据模型。 ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • EPPlus绘制刻度线的方法及示例代码
    本文介绍了使用EPPlus绘制刻度线的方法,并提供了示例代码。通过ExcelPackage类和List对象,可以实现在Excel中绘制刻度线的功能。具体的方法和示例代码在文章中进行了详细的介绍和演示。 ... [详细]
  • Go冒泡排序练习
    package main要求:随机生成5个元素的数组,并使用冒泡排序对其排序  从小到大思路分析:随机数用mathrand生成为了更好 ... [详细]
  • Channel是Go中的一个核心类型,你可以把它看成一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯(communication)。类型T表示任意的一种类型双向:chan ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 1Lock与ReadWriteLock1.1LockpublicinterfaceLock{voidlock();voidlockInterruptibl ... [详细]
author-avatar
手机用户2502886335
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有