news 2026/10/21 15:29:35

Python五子棋实战源码:Tkinter GUI开发与坐标映射详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python五子棋实战源码:Tkinter GUI开发与坐标映射详解

简介:本资源是一份面向计算机专业本科生的Python毕业设计完整实践材料,聚焦五子棋游戏开发全流程,帮助学习者掌握GUI编程、游戏逻辑设计与软件工程规范。压缩包含1个253KB的Word文档(.doc),完整涵盖摘要、需求分析、可行性研究、系统结构设计、Tkinter界面实现(Canvas绘图+for循环控制)、胜负判定算法、主界面与胜利提示界面展示、系统测试方案及问题解决记录等核心章节,内容详实、结构清晰,适合作为课程设计参考或自学项目范本。已有629人学习下载,读者可直接复现人机/双人对战功能,深入理解可视化模块、玩家操作响应机制与连珠检测逻辑,并借鉴其测试用例设计与代码优化思路。

1. 这不是玩具代码:一份能跑通、能调试、能交毕设的 Python 五子棋实战源码包

你是不是也经历过——在毕设开题时信心满满,写完“基于 Python 的五子棋”八个字,结果卡在 Tkinter 坐标映射三天?点鼠标落子,黑棋下完白棋不响应;五子连珠了,界面静默如初;想加个悔棋按钮,发现坐标存的全是像素值,根本对不上棋盘交点……这不是玄学,是典型 GUI 游戏开发落地断层:论文里写着“采用 canvas 绘制标准 15×15 棋盘”,但源码里pieces_x = [i for i in range(32, 523, 35)]这行硬编码,没人告诉你 35 是线距、32 是左上角偏移、523 是右下边界——而这些,恰恰是调试时翻车最狠的三处锚点。

这份名为《基于 python 五子棋的设计与实现》的毕业设计资源,不是 PDF 论文截图+压缩包里一堆.py文件的拼凑体。它是一套完整闭环的可执行工程:从tkinter窗体初始化、棋盘网格坐标系构建、落子事件绑定、双色轮转控制,到横/纵/斜四向五连珠判定(含边界防越界)、胜负弹窗触发、重置逻辑清空状态——全部用原生 Python 实现,无第三方游戏引擎依赖,零环境配置即可双击运行。它专为两类人准备:一是正在赶毕设 deadline 的本科生,需要一份结构清晰、注释到位、能过答辩、能现场演示的参考实现;二是想夯实 GUI 交互底层逻辑的初学者,它把“鼠标坐标 → 棋盘交点 → 落子存储 → 连珠扫描 → 状态更新 → UI 刷新”这条链路,拆解成 7 个可逐行调试的函数模块。别被标题里的“论文”二字迷惑——真正值钱的是附在文末的 18 页源码,那才是你调试到凌晨三点还能靠得住的“后悔药”。


2. 从 tkinter 窗体到 15×15 棋盘:坐标系构建与可视化模块落地

2.1 窗体初始化与棋盘网格生成:为什么是range(32, 523, 35)?

论文里轻描淡写一句“设置棋盘为十五路”,但实际编码中,棋盘不是画出来的,是算出来的。Tkinter 的Canvas组件本身不提供“格子”概念,所有交点坐标必须手动计算。该源码采用等距网格法,核心参数如下:

PIECE_SIZE = 10 # 棋子半径,影响落子视觉大小 pieces_x = [i for i in range(32, 523, 35)] # 横向15个交点x坐标 pieces_y = [i for i in range(38, 529, 35)] # 纵向15个交点y坐标

参数说明:

  • 32和38是棋盘左上角交点的起始坐标(非窗体左上角),由窗体边框、标题栏、侧边提示区高度共同决定;
  • 35是相邻交点的像素间距,即“一路”的物理长度,直接决定棋盘紧凑度;
  • 523和529是右下角交点坐标,计算公式为32 + 35*(15-1) = 522(取整为 523),38 + 35*(15-1) = 528(取整为 529)。
    关键逻辑:pieces_x和pieces_y构成笛卡尔积,生成全部15×15=225个合法落子点坐标。后续所有落子判断,都基于此坐标集进行最近邻匹配。

2.2 Canvas 绘制棋盘:线条、交点与动态提示的三层叠加

棋盘可视化不是简单画 15 条横线+15 条竖线。该源码采用分层绘制策略,确保交互响应精准:

# 1. 绘制棋盘背景(浅米色) canvas.create_rectangle(0, 0, 600, 600, fill="#f5f5dc", outline="") # 2. 绘制15×15网格线(深灰色) for i in range(15): # 横线:y坐标固定,x从pieces_x[0]到pieces_x[-1] canvas.create_line(pieces_x[0], pieces_y[i], pieces_x[-1], pieces_y[i], fill="#333", width=1) # 竖线:x坐标固定,y从pieces_y[0]到pieces_y[-1] canvas.create_line(pieces_x[i], pieces_y[0], pieces_x[i], pieces_y[-1], fill="#333", width=1) # 3. 绘制星位(天元与四隅)——增强专业感 star_points = [(3,3), (3,11), (11,3), (11,11), (7,7)] # 索引从0开始 for x_idx, y_idx in star_points: cx, cy = pieces_x[x_idx], pieces_y[y_idx] canvas.create_oval(cx-3, cy-3, cx+3, cy+3, fill="black")

逻辑说明:

  • 背景矩形覆盖全画布,避免默认白色刺眼;
  • 网格线使用width=1保证清晰不粗重,fill="#333"比纯黑更柔和;
  • 星位用小黑圆模拟真实棋盘,坐标通过索引转换,而非硬编码像素值,便于后期调整棋盘大小。
    调试技巧:若发现落子位置偏移,优先检查pieces_x/pieces_y起始值是否与create_rectangle的画布尺寸匹配——这是新手踩坑率最高的地方。

2.3 动态玩家提示区:side_canvas 的独立作用域设计

论文提到“界面中间‘黑方’与‘落子’二处文字为游戏进行中的提示信息”,但源码实际采用双 Canvas 架构:主canvas负责棋盘与棋子,侧边side_canvas(宽 150px)专用于状态提示。这种分离设计规避了tag冲突:

# 侧边提示区(独立Canvas) side_canvas = tk.Canvas(root, width=150, height=600, bg="#e6f7ff") side_canvas.pack(side="right", fill="y") # 绘制当前执棋方提示(带棋子图标) def zhangsiyuan(color): global piece_color piece_color = color side_canvas.delete("show_piece") # 仅清除棋子图标,不影响文字 side_canvas.create_oval(110 - PIECE_SIZE, 25 - PIECE_SIZE, 110 + PIECE_SIZE, 25 + PIECE_SIZE, fill=color, tags=("show_piece")) # tag锁定清除范围 # 文字提示绑定StringVar var = tk.StringVar() var.set("执黑棋") label = tk.Label(side_canvas, textvariable=var, font=("SimSun", 14)) label.place(x=20, y=20)

参数说明:

  • side_canvas尺寸固定,不受主画布缩放影响,保证提示区稳定;
  • tags=("show_piece")是关键——delete()时只删带此 tag 的对象,文字label完全不受影响;
  • 110是侧边区水平居中坐标,25是垂直位置,与PIECE_SIZE=10配合,使棋子图标居中显示。
    进阶价值:此设计为后续扩展(如添加计时器、胜率统计)预留了干净的 UI 插槽,无需重构主画布。

3. 落子逻辑与双色轮转:从鼠标事件到坐标映射的完整链路

3.1 鼠标点击事件绑定:bind("<Button-1>", zsy)的深层含义

GUI 游戏的核心交互始于鼠标点击。该源码未使用Button组件模拟落子,而是直接监听画布点击,原因在于:像素级精度控制。<Button-1>表示左键单击,zsy函数是事件处理器入口:

# 主画布绑定点击事件 canvas.bind("<Button-1>", zsy) # 事件处理器:获取鼠标坐标并触发落子 def zsy(event): global click_x, click_y click_x = event.x # 窗口坐标系下的x click_y = event.y # 窗口坐标系下的y yyy() # 进入坐标映射与落子流程

关键逻辑:event.x/event.y返回的是相对于画布左上角的像素坐标,而非屏幕全局坐标。这决定了后续所有映射必须以画布为基准,与窗体尺寸无关。若你尝试在root上绑定事件,会因标题栏、边框导致坐标偏移,这是常见翻车点。

3.2 坐标映射函数yyy():从像素点到棋盘交点的最近邻算法

鼠标点击的(click_x, click_y)是浮点像素值,而合法落子点只能是pieces_x × pieces_y中的整数坐标。yyy()函数执行最近邻搜索,并校验合法性:

def yyy(): global click_x, click_y, person_flag, coor_black, coor_white coor = coor_black + coor_white # 所有已落子坐标 item = canvas.find_closest(click_x, click_y) # Tkinter内置查找最近图形项 tags_tuple = canvas.gettags(item) # 获取该图形项的tags if len(tags_tuple) > 1: # 确保找到的是交点(交点被赋予了坐标tag) tags_list = list(tags_tuple) coor_list = tags_list[:2] # 前两个tag存x,y坐标字符串 try: for i in range(len(coor_list)): coor_list[i] = int(coor_list[i]) # 转换为整数 except ValueError: pass # 若tag非数字,跳过 else: coor_tuple = tuple(coor_list) (click_x, click_y) = coor_tuple # 更新为最近交点坐标 # 关键校验:该点未被占用,且在棋盘范围内 if ((click_x, click_y) not in coor and click_x in pieces_x and click_y in pieces_y): if person_flag != 0: # 游戏未结束 if person_flag == 1: # 黑方回合 aaa("black") # 执行落子 zhangsiyuan("white") # 切换提示 var.set("执白棋") elif person_flag == -1: # 白方回合 aaa("white") zhangsiyuan("black") var.set("执黑棋") person_flag *= -1 # 轮转标志位

参数说明与避坑点:

  • canvas.find_closest()返回的是画布上距离鼠标点最近的图形对象ID,而非坐标。需配合gettags()提取预设的坐标标签;
  • tags_tuple中前两个元素必须是字符串格式的坐标(如"32"、"38"),这是源码在绘制交点时主动打上的 tag,若你删除了交点绘制代码,此函数将失效;
  • if (click_x, click_y) not in coor是防重复落子的关键,coor是黑白棋坐标列表的合并,确保同一交点不可二次落子;
  • person_flag是状态机核心:1表示黑方回合,-1表示白方回合,0表示游戏结束。轮转通过*= -1实现,简洁且不易出错。

3.3 落子函数aaa():绘图、存储、判定三位一体

aaa()不仅画棋子,还负责坐标存储与触发胜负判定,是落子逻辑的原子操作:

def aaa(piece_color): global coor_black, coor_white, click_x, click_y # 1. 绘制棋子(带颜色填充) canvas.create_oval(click_x - PIECE_SIZE, click_y - PIECE_SIZE, click_x + PIECE_SIZE, click_y + PIECE_SIZE, fill=piece_color, tags=("piece")) # tag统一管理棋子 # 2. 存储坐标到对应颜色列表 if piece_color == "white": coor_white.append((click_x, click_y)) elif piece_color == "black": coor_black.append((click_x, click_y)) # 3. 立即触发该颜色棋子的胜负判定 feifei(piece_color)

逻辑说明:

  • tags=("piece")为所有棋子打上统一标签,canvas.delete("piece")即可一键清空棋盘,xiaofei()重置函数正是利用此特性;
  • 坐标存储采用(x, y)元组,与pieces_x/pieces_y值域一致,确保后续判定函数可直接遍历;
  • feifei(piece_color)是判定入口,传入当前落子颜色,避免全量扫描,提升效率。
    性能提示:对于 15×15 棋盘,单次判定最多扫描 225 个点,O(n) 复杂度完全可接受,无需优化。

4. 五连珠判定算法:横纵斜四向扫描与边界安全防护

4.1 判定架构总览:feifei→xiaozhang→xiaoyuan/guixin的责任链

论文中“胜负判定模块”描述抽象,而源码实现了清晰的职责分离:

  • feifei(piece_color):判定调度器,根据落子颜色分发任务;
  • xiaozhang(coor):判定协调器,汇总横纵斜四向结果;
  • xiaoyuan(coor):横纵轴扫描器(水平、垂直);
  • guixin(coor):斜向扫描器(左上-右下、右上-左下);
  • yuan(coor, pieces_count, t1, t2):基础计数器,沿指定方向递归计数。

这种分层设计使代码可读性极高,每个函数专注单一职责,便于单元测试和调试。

4.2 横纵轴判定xiaoyuan():方向向量与边界终止条件

xiaoyuan()处理水平(t1=1,t2=0)和垂直(t1=0,t2=1)方向,核心是双向延伸扫描:

def xiaoyuan(coor): pieces_count = 0 # 向右扫描(水平正向) pieces_count = yuan(coor, pieces_count, 1, 0) # t1=1, t2=0 表示x增y不变 # 向左扫描(水平反向) pieces_count = yuan(coor, pieces_count, -1, 0) # t1=-1, t2=0 if pieces_count >= 4: # 已达5子(含当前子) return 1 # 向上扫描(垂直反向) pieces_count = 0 pieces_count = yuan(coor, pieces_count, 0, -1) # t1=0, t2=-1 # 向下扫描(垂直正向) pieces_count = yuan(coor, pieces_count, 0, 1) # t1=0, t2=1 if pieces_count >= 4: return 1 else: return 0

关键逻辑:

  • pieces_count初始化为 0,每次调用yuan()前重置,确保各方向独立计数;
  • >=4是判定阈值:当前落子为第1子,需在任一方向找到连续4颗同色棋子,构成5连;
  • 方向向量(t1, t2)控制坐标增量,35是棋盘线距,故x += t1*35*i,y += t2*35*i。

4.3 斜向判定guixin():两组对角线的全覆盖扫描

斜向判定需处理两组对角线,guixin()通过两轮yuan()调用实现:

def guixin(coor): pieces_count = 0 # 右下方向(左上→右下对角线) pieces_count = yuan(coor, pieces_count, 1, 1) # x增y增 # 左上方向(同一线,反向延伸) pieces_count = yuan(coor, pieces_count, -1, -1) # x减y减 if pieces_count >= 4: return 1 # 右上方向(左下→右上对角线) pieces_count = 0 pieces_count = yuan(coor, pieces_count, 1, -1) # x增y减 # 左下方向(同一线,反向延伸) pieces_count = yuan(coor, pieces_count, -1, 1) # x减y增 if pieces_count >= 4: return 1 else: return 0

边界防护重点:yuan()函数内部包含严格的越界检查:

def yuan(coor, pieces_count, t1, t2): for i in range(1, 5): # 最多查4步(5子含当前子) (x, y) = (click_x + t1 * 35 * i, click_y + t2 * 35 * i) if (x, y) in coor: # 仅当坐标存在于已落子列表才计数 pieces_count += 1 else: break # 遇到空位或越界,立即终止该方向 return pieces_count

此设计天然规避数组越界:不依赖索引,只判断(x,y)是否在coor列表中,即使计算出的(x,y)超出pieces_x/pieces_y范围,in操作返回False,循环自动退出。


5. 避坑指南:七个真实踩过的雷与血泪解决方案

注意:以下问题均来自实际调试过程,非理论假设。每一条都对应源码中一个具体函数或参数,照着改就能解决。

5.1 现象:点击落子后,永远只有黑棋出现,白棋不响应

原因:person_flag初始值为1,但yyy()函数中轮转逻辑person_flag *= -1在首次落子后变为-1,而aaa()调用feifei()后,xiaozhang()中的person_flag = 0将其置零,导致后续if person_flag != 0判断失败,白棋失去落子资格。
解决:检查siyuan()函数(胜负提示)是否在游戏未结束时被误调用。源码中siyuan()仅在xiaozhang()判定胜利后执行,但若feifei()被错误调用多次,可能提前触发。修复方法:确保feifei()只在aaa()中调用一次,且xiaozhang()中person_flag = 0仅在if xiaoyuan(...) == 1 or guixin(...) == 1:成立时执行。

5.2 现象:五子连珠已形成,但无胜利提示,游戏继续

原因:xiaoyuan()和guixin()的返回值未被正确捕获。源码中xiaozhang(coor)的调用写为if xiaoyuan(coor) == 1 or guixin(coor) == 1:,但若xiaoyuan()因语法错误返回None,None == 1为False,判定失效。
解决:在xiaoyuan()和guixin()函数末尾强制添加return 0(如原文第14页所示),确保所有分支都有返回值。Python 函数默认返回None,这是静默失败的根源。

5.3 现象:悔棋功能点击无反应,或悔棋后棋盘残留棋子

原因:源码中未提供悔棋按钮的完整实现,仅在需求分析中提及。若自行添加按钮,需同步操作:1)从coor_black/coor_white中删除最后落子坐标;2)canvas.delete()对应棋子(需记录棋子ID);3)恢复person_flag状态。
解决:在aaa()函数中,canvas.create_oval()返回图形ID,应将其与坐标一同存储。例如:

piece_id = canvas.create_oval(...) if piece_color == "black": coor_black.append((click_x, click_y, piece_id)) # 存ID # 悔棋时:canvas.delete(coor_black[-1][2]); coor_black.pop()

5.4 现象:窗口缩放后,棋盘变形,落子点漂移

原因:pieces_x/pieces_y是静态列表,未随窗体尺寸变化动态重算。Tkinter 默认不响应窗体缩放事件。
解决:为root绑定<Configure>事件,在回调中重新生成pieces_x/pieces_y,并重绘棋盘。但需注意:毕业设计通常要求固定窗口尺寸,更稳妥的做法是在root.geometry("750x600")中硬编码窗体大小,禁用缩放:root.resizable(False, False)。

5.5 现象:启动程序后,侧边提示区“执黑棋”文字不显示,或显示错位

原因:side_canvas创建后,label.place()的坐标(x=20, y=20)是相对于side_canvas左上角,但若side_canvas尚未渲染完成,place()可能失效。
解决:将label.place()移至side_canvas.pack()之后,并添加side_canvas.update()强制刷新:

side_canvas.pack(side="right", fill="y") side_canvas.update() # 确保尺寸确定 label.place(x=20, y=20)

6. 远程调试实战:用 VS Code + Python Debugger 深度剖析五子棋状态流

6.1 配置 launch.json:让断点精准命中yyy()和feifei()

VS Code 的 Python 调试能力远超 IDLE,尤其适合跟踪 GUI 状态流。在项目根目录创建.vscode/launch.json:

{ "version": "0.2.0", "configurations": [ { "name": "Python: Current File", "type": "python", "request": "launch", "module": "tkinter", "console": "integratedTerminal", "justMyCode": true, "env": { "PYTHONPATH": "${workspaceFolder}" } } ] }

关键配置说明:

  • "module": "tkinter"确保调试器加载 Tkinter 模块符号;
  • "console": "integratedTerminal"将输出重定向到 VS Code 终端,避免print()被 GUI 阻塞;
  • "justMyCode": true过滤掉 Tkinter 底层代码,聚焦你的逻辑。

6.2 断点设置策略:在四个黄金位置插入观察点

不要盲目打断点。针对五子棋状态流,以下四点是必设观察位:

断点位置触发时机观察目标调试价值
zsy(event)开头鼠标点击瞬间event.x,event.y值验证原始坐标是否合理,排除窗体偏移
yyy()中coor_tuple = tuple(coor_list)后坐标映射完成click_x,click_y是否为pieces_x/pieces_y中的合法值确认映射算法正确性,定位“点不准”问题
aaa()中coor_black.append(...)后黑棋落子存储后len(coor_black)是否递增,coor_black[-1]值是否匹配click_x/click_y验证存储逻辑,排查坐标存错
feifei()中xiaozhang(coor_black)前判定前一刻coor_black列表内容,特别是最后5个坐标分析连珠模式,人工验证判定逻辑

6.3 使用 Debug Console 实时注入调试指令

VS Code 调试器的 Console 支持运行任意 Python 代码。当断点停在feifei("black")时,可在 Console 输入:

# 查看当前黑棋所有坐标(按x排序,便于观察横排) sorted(coor_black, key=lambda p: p[0]) # 检查(32,38)附近是否有4颗黑棋(模拟横排五连) nearby = [(x,y) for x,y in coor_black if abs(x-32)<10 and abs(y-38)<10] len(nearby) # 应返回1(仅当前子) # 强制触发胜负判定(跳过鼠标事件) xiaozhang(coor_black) # 若返回1,说明判定逻辑正常

技巧:sorted()和列表推导式是分析坐标分布的利器。比肉眼数坐标高效十倍,且避免遗漏。

6.4 保存调试状态:用pprint输出完整游戏快照

在siyuan()函数开头添加快照打印,永久记录胜利时刻状态:

import pprint def siyuan(): # ... 原有代码 # 新增:保存当前完整状态到文件 state_snapshot = { "coor_black": coor_black, "coor_white": coor_white, "person_flag": person_flag, "winning_direction": "horizontal" if xiaoyuan(coor_black)==1 else "diagonal" } with open("game_snapshot.txt", "a") as f: f.write(f"\n=== Victory at {time.time()} ===\n") pprint.pprint(state_snapshot, stream=f)

价值:当答辩老师问“你是怎么验证五连珠判定准确的?”,你可以直接打开game_snapshot.txt,展示某次胜利时coor_black中连续5个x坐标差值为35的证据。这比口头解释有力百倍。

从那以后我每次调试 GUI 游戏,都强制走一遍 VS Code 的断点注入+Console 实时分析流程。它逼我直面坐标、状态、数据流的真实关系,而不是在print()的海洋里溺水。这份五子棋源码的价值,不在它多炫酷,而在于它足够“脏”——有硬编码、有未完成的悔棋、有person_flag的脆弱状态机。正是这些不完美,让它成为一面镜子,照见我们从写代码到懂系统的真实路径。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 15:29:11

把个人技能当系统来维护:从盘点、评分到高效成长的完整方法

1. "skills"不是简历上那三行字&#xff0c;而是一个需要维护的系统前两年我做过一次很尴尬的复盘。当时想跳槽&#xff0c;打开简历准备更新技能栏&#xff0c;盯着屏幕想了二十分钟&#xff0c;写出来的是"熟悉办公软件、掌握某种编程语言、了解数据库"。…

作者头像 李华
网站建设 2026/10/21 15:28:15

Halcon血管支架视觉检测:高精度测量与缺陷识别全解析

血管支架直径只有几毫米&#xff0c;上面的金属网丝细得像头发丝&#xff0c;公差却常常要求在几十微米以内。这种零件放到产线上做全检&#xff0c;靠人眼盯显微镜不现实&#xff0c;靠传统接触式测量又容易划伤表面。所以这几年很多医疗器械厂家在找机器视觉方案&#xff0c;…

作者头像 李华
网站建设 2026/10/21 15:27:43

给API密钥加把锁:访问密码保护与加密配置实战

前几个月我用某AI集成工具对接绘图平台的配置时&#xff0c;踩了一个让我记忆深刻的坑&#xff1a;项目做完准备交付给同事&#xff0c;结果发现API密钥直接以明文形式躺在配置文件的角落里。谁拿到这个文件&#xff0c;谁就能调用我账户下所有的AI能力&#xff0c;账单和配额全…

作者头像 李华
网站建设 2026/10/21 15:26:28

lingbot-map:低成本机器人建图与定位实战解析

看到 lingbot-map 这个项目名&#xff0c;我第一反应是这应该是一套机器人相关的地图构建与导航定位系统。Ling 可以理解为灵巧或智能的谐音&#xff0c;Bot 代表机器人实体&#xff0c;Map 则是整个系统中最核心的空间感知模块。做机器人项目的人都清楚&#xff0c;地图这件事…

作者头像 李华
网站建设 2026/10/21 15:25:04

REA建模实战:用资源-事件-主体模型终结订单对账难题

很多团队做业务系统做久了&#xff0c;都会走到一个很相似的死局&#xff1a;订单、库存、资金、优惠券、积分各自维护一套流水&#xff0c;单独看都像模像样&#xff0c;合在一起月底对账就成了全组最怕的事。我在重构某个零售模拟项目的时候&#xff0c;把业务数据从“流水叠…

作者头像 李华
网站建设 2026/10/21 15:24:29

银行家算法详解:从死锁避免到安全状态检查的完整实现

1. 死锁是怎么发生的&#xff1a;从一次卡死的并发现场说起 我见过不少项目在并发场景下突然“冻住”&#xff1a;几个线程各自拿着资源不放&#xff0c;又都在等对方手里的资源&#xff0c;谁都不让&#xff0c;整个系统就像两辆车在窄桥上顶牛。这种局面在操作系统里叫死锁&a…

作者头像 李华