当前位置: 首页 > news >正文

令牌环式同步扩展

令牌环式同步扩展:从 Ping-Pong 到 ABC 交替执行

在上一篇博客中,我们介绍了六种实现两个线程交替执行的方法。本文将作为扩展,探讨如何将这些实现方式改造为三个线程交替执行 "A"、"B"、"C",形成 "ABCABC..." 的环形序列。

问题描述

实现三个线程交替打印 "A"、"B"、"C",共打印 100 轮,形成 "ABCABC..." 的交替序列。

六种实现方式的改造分析

1. ManualResetEvent / AutoResetEvent 实现

改造难度:⭐⭐ 容易

internal class ABC_EventTestCode
{public static void Print(){var a = new AutoResetEvent(true);  // A 初始可用var b = new AutoResetEvent(false); // B 初始不可用var c = new AutoResetEvent(false); // C 初始不可用// 线程 ATask.Run(() =>{for (int i = 0; i < 100; i++){a.WaitOne();    // 等待 A 信号Console.WriteLine("A");b.Set();        // 释放 B 信号}});// 线程 BTask.Run(() =>{for (int i = 0; i < 100; i++){b.WaitOne();    // 等待 B 信号Console.WriteLine("B");c.Set();        // 释放 C 信号}});// 线程 CTask.Run(() =>{for (int i = 0; i < 100; i++){c.WaitOne();    // 等待 C 信号Console.WriteLine("C");a.Set();        // 释放 A 信号,形成闭环}});}
}

工作原理

  • 使用三个 AutoResetEvent 对象,初始状态分别为 true(A 可用)、false(B 不可用)和 false(C 不可用)
  • 每个线程执行前调用 WaitOne() 等待信号
  • 执行完成后,通过 Set() 通知下一个线程,最后一个线程通知第一个线程形成闭环

优缺点

  • ✅ 改造简单,逻辑清晰
  • ✅ AutoResetEvent 自动重置,无需手动调用 Reset()
  • ✅ 适用于传统同步场景
  • ❌ 不支持异步编程,会阻塞线程
  • ❌ ManualResetEvent 需要手动 Reset,三个信号时容易漏掉

2. SemaphoreSlim 实现

改造难度:⭐⭐ 容易

internal class ABC_SemaphoreSlimTestCode
{// A 初始可用(1),B、C 初始不可用(0)private SemaphoreSlim semA = new SemaphoreSlim(1, 1);private SemaphoreSlim semB = new SemaphoreSlim(0, 1);private SemaphoreSlim semC = new SemaphoreSlim(0, 1);public void Print(){// 线程 ATask.Run(async () =>{for (int i = 0; i < 100; i++){await semA.WaitAsync();   // 等待 A 信号Console.WriteLine("A");semB.Release();           // 释放 B 信号}});// 线程 BTask.Run(async () =>{for (int i = 0; i < 100; i++){await semB.WaitAsync();   // 等待 B 信号Console.WriteLine("B");semC.Release();           // 释放 C 信号}});// 线程 CTask.Run(async () =>{for (int i = 0; i < 100; i++){await semC.WaitAsync();   // 等待 C 信号Console.WriteLine("C");semA.Release();           // 释放 A 信号,形成闭环}});}
}

工作原理

  • 使用三个 SemaphoreSlim 对象,初始计数分别为 1(A 可用)、0(B 不可用)和 0(C 不可用)
  • 每个线程执行前调用 WaitAsync() 等待信号
  • 执行完成后,通过 Release() 增加下一个信号量的计数,最后一个线程释放第一个信号量形成闭环

优缺点

  • ✅ 改造简单,代码简洁
  • ✅ 支持异步编程,使用 await 语法更现代
  • ✅ 轻量级,性能较好
  • ✅ 最适合 ABC 场景,代码清晰易维护
  • ✅ 支持超时和取消操作

3. TaskCompletionSource 实现

改造难度:⭐⭐⭐ 中等

internal class ABC_TaskCompletionSourceTestCode
{public static async Task PrintAsync(){var tcsA = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);var tcsB = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);var tcsC = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);// 初始让 A 可以执行tcsA.SetResult(true);// 线程 Avar taskA = Task.Run(async () =>{for (int i = 0; i < 100; i++){await tcsA.Task;     // 等待 A 信号Console.WriteLine("A");tcsA = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);tcsB.SetResult(true); // 通知 B 可以执行}});// 线程 Bvar taskB = Task.Run(async () =>{for (int i = 0; i < 100; i++){await tcsB.Task;     // 等待 B 信号Console.WriteLine("B");tcsB = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);tcsC.SetResult(true); // 通知 C 可以执行}});// 线程 Cvar taskC = Task.Run(async () =>{for (int i = 0; i < 100; i++){await tcsC.Task;     // 等待 C 信号Console.WriteLine("C");tcsC = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);tcsA.SetResult(true); // 通知 A 可以执行,形成闭环}});await Task.WhenAll(taskA, taskB, taskC);}
}

工作原理

  • 使用三个 TaskCompletionSource 对象
  • 初始时,将 A 的任务标记为完成,允许 A 立即执行
  • 每个线程执行前等待对应的 Task 完成
  • 执行完成后,创建新的 TaskCompletionSource 对象,并将下一个线程的任务标记为完成,最后一个线程通知第一个线程形成闭环

优缺点

  • ✅ 支持异步编程模型
  • ✅ 可以传递实际数据,不仅仅是信号
  • ❌ 每次迭代都需要创建三个新的 TaskCompletionSource 对象,内存开销较大
  • ❌ 代码相对复杂,需要理解异步编程模型
  • ❌ 对于纯信号控制场景,有点杀鸡用牛刀

4. Channel 实现

改造难度:⭐⭐ 容易

internal class ABC_ChannelTestCode
{public static async Task PrintAsync(){var chA = Channel.CreateUnbounded<bool>();var chB = Channel.CreateUnbounded<bool>();var chC = Channel.CreateUnbounded<bool>();// 初始让 A 可以执行await chA.Writer.WriteAsync(true);// 线程 Avar taskA = Task.Run(async () =>{for (int i = 0; i < 100; i++){await chA.Reader.ReadAsync(); // 等待 A 信号Console.WriteLine("A");await chB.Writer.WriteAsync(true); // 通知 B 可以执行}});// 线程 Bvar taskB = Task.Run(async () =>{for (int i = 0; i < 100; i++){await chB.Reader.ReadAsync(); // 等待 B 信号Console.WriteLine("B");await chC.Writer.WriteAsync(true); // 通知 C 可以执行}});// 线程 Cvar taskC = Task.Run(async () =>{for (int i = 0; i < 100; i++){await chC.Reader.ReadAsync(); // 等待 C 信号Console.WriteLine("C");await chA.Writer.WriteAsync(true); // 通知 A 可以执行,形成闭环}});await Task.WhenAll(taskA, taskB, taskC);}
}

工作原理

  • 使用三个 Channel 对象创建无界通道
  • 初始时,向 A 通道写入数据,允许 A 立即执行
  • 每个线程从自己的通道读取数据(等待信号)
  • 执行完成后,向下一个通道写入数据(发送信号),最后一个线程向第一个通道写入数据形成闭环

优缺点

  • ✅ 现代异步编程模型,设计优雅
  • ✅ 高性能,适合高并发场景
  • ✅ 可以轻松扩展为传递复杂数据
  • ❌ 需要 .NET Core 3.0+ 或 .NET 5+ 支持
  • ❌ 对于纯信号控制场景,可能显得过于复杂

5. Monitor/lock 实现

改造难度:⭐⭐⭐⭐ 困难

internal class ABC_MonitorLockTestCode
{private object lockObj = new object();private int turn = 0; // 0=A, 1=B, 2=Cpublic void Print(){// 线程 ATask.Run(() =>{for (int i = 0; i < 100; i++){lock (lockObj){while (turn != 0) // 等待自己的回合{Monitor.Wait(lockObj); // 释放锁并等待信号}Console.WriteLine("A");turn = 1; // 切换到 B 回合Monitor.PulseAll(lockObj); // 唤醒所有等待的线程}}});// 线程 BTask.Run(() =>{for (int i = 0; i < 100; i++){lock (lockObj){while (turn != 1) // 等待自己的回合{Monitor.Wait(lockObj); // 释放锁并等待信号}Console.WriteLine("B");turn = 2; // 切换到 C 回合Monitor.PulseAll(lockObj); // 唤醒所有等待的线程}}});// 线程 CTask.Run(() =>{for (int i = 0; i < 100; i++){lock (lockObj){while (turn != 2) // 等待自己的回合{Monitor.Wait(lockObj); // 释放锁并等待信号}Console.WriteLine("C");turn = 0; // 切换到 A 回合,形成闭环Monitor.PulseAll(lockObj); // 唤醒所有等待的线程}}});}
}

工作原理

  • 使用一个共享的 lockObj 作为同步对象
  • 使用一个整数变量 turn 来跟踪当前应该执行的线程(0=A, 1=B, 2=C)
  • 每个线程在执行前检查是否是自己的回合,如果不是则等待
  • 执行完成后,切换回合状态并唤醒所有等待的线程

优缺点

  • ✅ 不需要额外的同步原语,只使用 .NET 内置的 Monitor 机制
  • ❌ 改造复杂,代码冗长
  • ❌ 不支持异步编程,会阻塞线程
  • ❌ 需要使用 PulseAll() 唤醒所有线程,性能较差
  • ❌ 三个线程竞争锁,容易导致不必要的唤醒
  • ❌ 代码复杂度指数增长,难以维护

6. 混合方案:流水线 Channel 实现

改造难度:⭐⭐ 容易

internal class ABC_PipelineChannelTestCode
{public static async Task PrintAsync(){// 创建两个通道,形成 A → B → C 的流水线var abChannel = Channel.CreateUnbounded<string>();var bcChannel = Channel.CreateUnbounded<string>();// 线程 A - 生产数据var taskA = Task.Run(async () =>{for (int i = 0; i < 100; i++){string data = $"A-{i}";Console.WriteLine($"A: {data}");await abChannel.Writer.WriteAsync(data); // 传递给 B}abChannel.Writer.Complete(); // 完成写入});// 线程 B - 处理数据var taskB = Task.Run(async () =>{await foreach (var data in abChannel.Reader.ReadAllAsync()){string processedData = $"B-{data}";Console.WriteLine($"B: {processedData}");await bcChannel.Writer.WriteAsync(processedData); // 传递给 C}bcChannel.Writer.Complete(); // 完成写入});// 线程 C - 消费数据var taskC = Task.Run(async () =>{await foreach (var data in bcChannel.Reader.ReadAllAsync()){string finalData = $"C-{data}";Console.WriteLine($"C: {finalData}");}});await Task.WhenAll(taskA, taskB, taskC);}
}

工作原理

  • 使用两个 Channel 对象创建流水线
  • 线程 A 生产数据并写入第一个通道
  • 线程 B 从第一个通道读取数据,处理后写入第二个通道
  • 线程 C 从第二个通道读取数据并消费

优缺点

  • ✅ 非常适合需要传递数据的场景
  • ✅ 现代异步编程模型,设计优雅
  • ✅ 高性能,适合高并发场景
  • ✅ 代码逻辑清晰,易于理解
  • ❌ 对于纯信号控制场景,可能显得过于复杂
  • ❌ 需要 .NET Core 3.0+ 或 .NET 5+ 支持

实现方式对比

实现方式 改造难度 ABC 适合度 推荐场景
AutoResetEvent ⭐⭐ ⭐⭐⭐ 传统同步,无 async/await
SemaphoreSlim ⭐⭐ ⭐⭐⭐⭐⭐ 现代异步,首选
TaskCompletionSource ⭐⭐⭐ ⭐⭐ 需传递复杂数据
Channel ⭐⭐ ⭐⭐⭐ 高并发+数据传递
Monitor/lock ⭐⭐⭐⭐ 简单场景,避免多信号
流水线 Channel ⭐⭐ ⭐⭐⭐⭐⭐ 数据流转(A→B→C)

通用模式:扩展到 N 个线程

使用 SemaphoreSlim 可以很容易地扩展到 N 个线程的交替执行。以下是一个通用的实现模式:

internal class NThreadsSemaphoreSlimTestCode
{private List<SemaphoreSlim> semaphores;private int threadCount;public NThreadsSemaphoreSlimTestCode(int count){threadCount = count;semaphores = new List<SemaphoreSlim>();// 初始化信号量,第一个初始可用,其余初始不可用for (int i = 0; i < count; i++){semaphores.Add(new SemaphoreSlim(i == 0 ? 1 : 0, 1));}}public void Print(){for (int i = 0; i < threadCount; i++){int threadIndex = i;char threadChar = (char)('A' + threadIndex);Task.Run(async () =>{for (int j = 0; j < 100; j++){// 等待自己的信号await semaphores[threadIndex].WaitAsync();Console.WriteLine(threadChar);// 释放下一个线程的信号,最后一个线程释放第一个线程的信号int nextIndex = (threadIndex + 1) % threadCount;semaphores[nextIndex].Release();}});}}
}// 使用示例:
// var nThreadsTest = new NThreadsSemaphoreSlimTestCode(5); // 5个线程,交替打印 ABCDE
// nThreadsTest.Print();

结论

从两种线程扩展到三种线程(ABC)的交替执行,不同实现方式的表现差异更加明显:

  1. SemaphoreSlim 仍然是最佳选择,代码简洁、支持异步、性能良好,尤其是在多线程场景下优势更加突出。

  2. AutoResetEvent 也是一个不错的选择,改造简单,适合传统同步场景。

  3. Channel 在需要传递数据的场景下表现优异,尤其是流水线模式非常适合数据处理流程。

  4. TaskCompletionSource 虽然可以实现,但在多线程场景下代码膨胀严重,内存开销较大。

  5. Monitor/lock 在多线程场景下表现最差,代码复杂,性能较差,不推荐使用。

选择哪种实现方式,取决于具体的场景需求:

  • 纯信号控制(ABCABC):首选 SemaphoreSlim,代码最简洁,性能最好。
  • 需要传递数据:首选 Channel 流水线模式,尤其是在 A→B→C 需要处理数据的场景。
  • 传统同步场景:选择 AutoResetEvent,兼容性好,易于理解。
  • 特殊需求:根据具体情况选择其他实现方式。

通过本文的扩展,我们可以看到令牌环式同步模式的灵活性和多样性,以及不同同步原语在不同场景下的适用情况。了解这些实现方式,有助于我们在实际项目中选择合适的同步机制,编写高效、可靠的并发代码。

http://www.jsqmd.com/news/377993/

相关文章:

  • 大模型实习模拟面试:长上下文能力崛起,RAG真的会被淘汰吗?——一场关于信息检索与上下文处理的深度思辨
  • 2026年品牌全案公司推荐:中小企业增长痛点深度评测与实战排名 - 品牌推荐
  • H5大转盘抽奖系统源码|含后台管理、核销功能与抽奖码生成
  • 大模型实习模拟面试:当用户点击“拒绝”时,HITL 架构中 Host 端应抛出异常还是温柔反馈?——从安全拦截到人机协同的工程哲学
  • 大模型实习模拟面试:为什么已有 Function Calling 和 LangChain,我们仍需要 MCP?——AI 时代的通用接口革命
  • 2026年新初一补习观察:注重学习习惯培养的老师更受欢迎,新高一补课班/双语外教/成绩提升,新初一补习机构排行 - 品牌推荐师
  • 大模型实习模拟面试:RAG 幻觉根源剖析与工业级准确率提升四步法——从 60% 到 85% 的系统性突破
  • 大模型实习模拟面试:多智能体(Multi-Agent)协作机制深度解析——从角色分工到通信协议的全链路实战
  • 最早的汇编语言编译器用什么语言编写,二进制,随便自举编译(简单自身语言子集编写编译器,编译复杂的自身语言)以及编译器历史
  • 2026增压器领域优质厂家排行,选对品牌不踩雷,北汽2.0增压器/福康增压器/纽荷兰增压器,增压器组件有哪些 - 品牌推荐师
  • 最早的C语言编译器,先是B语言以及‌PDP-11汇编语言编写,后来通过自举,也就是简单的C语言子集作为编译器逐步通过其有限子集实现自我构建C语言编译自身C语言
  • 给你一张清单 10个降AI率软件降AIGC网站:专科生必看的降AI率工具测评与推荐
  • Windows Server 常用管理脚本(白帽子实战版)
  • 2026年深圳豆包GEO优化公司推荐,哪家品牌靠谱口碑好 - 工业品网
  • 2026年湖北省木材加工大型厂家排名,这些品牌性价比高值得推荐 - 工业设备
  • 学霸同款 8个降AIGC工具测评:专科生降AI率必看攻略
  • IP54与IP67有何区别?高防护三维扫描仪推荐指南 - 匠言榜单
  • 别再手动验参了!Flask动态路由的3个技巧,让URL校验自动化,效率提升300%
  • 工厂质量检测具体案例:从三维扫描到智能质检的落地路径 - 工业三维扫描仪评测
  • 2026成都冒菜加盟攻略:口碑品牌合作细节全公开,麻辣烫/冒菜/餐饮/冒菜店,冒菜加盟加盟推荐排行榜单 - 品牌推荐师
  • 2026年市面上靠谱的投影机厂家哪家权威,画展投影机出租/山体投影机/爱普生投影机出租,投影机品牌哪家好 - 品牌推荐师
  • 2026年重庆新华职业学校校企合作揭秘,证书认可度高吗值得探讨 - 工业推荐榜
  • 2026年广西抖音推广服务排名,在广西抖音推广选哪家好 - 工业品牌热点
  • 2026年河北帆布袋定制生产厂家排名,哪家性价比高 - myqiye
  • 2026年帆布袋定制供应商价格对比,京津冀靠谱的有哪些 - myqiye
  • 2026年合肥地区CAAC无人机培训专业机构排名,靠谱的品牌有哪些 - mypinpai
  • 2026年比较不错的高档私人医院设计专业公司,北京地区怎么收费 - 工业设备
  • 阐释2026年京津冀推荐商业空间设计施工厂商,靠谱的有哪些 - 工业品网
  • 聊聊去学计算机编程培训,兰州有哪些值得推荐的学校 - 工业推荐榜
  • 2026年东北三省彩色路面施工品牌推荐,辽宁拜而服务怎么样 - 工业设备