c# 在 C# 中模拟 goroutine 和 channel 的模式
技术百科
月夜之吻
发布时间:2026-01-27
浏览: 次 用 Task + Channel 替代 goroutine + channel的核心思路是:以 async/await 模拟 goroutine 的异步并发,用线程安全、真异步的 Channel 替代 channel;避免 BlockingCollection 和盲目 Task.Run,正确处理关闭、取消与资源清理。
用 Task + Channel 替代 goroutine + channel 的核心思路
Go 的 goroutine 本质是轻量级协作式并发单元,C# 没有语言级等价物,但 Task(配合 async/await)在绝大多数 I/O 密集场景下能达到相似效果:高并发、低栈开销、自动调度。而 Go 的 channel 在 .NET 6+ 中已有原生替代 —— System.Threading.Channels.Channel,它线程安全、支持异步读写、可配置缓冲策略,是目前最贴近的实现。
Channel 的创建与基础用法区别于 BlockingCollection
很多人第一反应是用 BlockingCollection,但它基于锁 + 阻塞,不支持真正的异步等待(TakeAsync 是伪异步),且无法取消;而 Channel 的 Writer.WriteAsync() 和 Reader.ReadAsync() 是真异步、可取消、无锁(单生产者/单消费者模式下)。
- 创建无界 channel:
var channel = Channel.CreateUnbounded
(); - 创建带容量限制的 channel:
var channel = Channel.CreateBounded
(new BoundedChannelOptions(100) { FullMode = BoundedChannelFullMode.Wait }); - 写入必须检查是否完成:
await channel.Writer.WriteAsync("hello"); // 不要忽略返回值 - 读取需处理关闭信号:
while (await channel.Reader.WaitToReadAsync()) { if (channel.Reader.TryRead(out var msg)) { /* 处理 msg */ } }
模拟 goroutine 启动:别直接 Task.Run,优先用 async + await
Go 的 go fn() 是隐式启动,C# 若盲目套用 Task.Run(() => { ... }),会把本该异步的 I/O 操作强行拉到线程池,浪费资源,还可能引发死锁(尤其在 UI 或 ASP.NET 同步上下文里)。正确做法是让工作函数本身是 async Task,再用 Task.Run 包裹仅当它含 CPU 密集逻辑时。
- I/O 密集型(如 HTTP 请求、文件读取):
async Task WorkerAsync(ChannelReader
reader) { while (await reader.WaitToReadAsync()) { if (reader.TryRead(out var msg)) { await SomeHttpCallAsync(msg); } } } - CPU 密集型(如图像处理)才考虑
Task.Run:Task.Run(() => HeavyCompute(msg));
- 启动多个“goroutine”风格协程:
_ = WorkerAsync(channel.Reader); _ = WorkerAsync(channel.Reader); // 注意:这里用 _ 忽略 Task 引用,实际应妥善管理生命周期
关闭 channel 和清理资源的常见漏点
Go 的 close(ch) 对应 C# 的 channel.Writer.Complete(),但容易被忽略的是:一旦调用 Complete(),后续所有 WriteAsync 都会抛 InvalidOperationException;且 Reader 不会自动退出循环,必须靠 WaitToReadAsync() 返回 false 才知道 channel 已关闭并写入完毕。
- 生产者结束前务必调用:
channel.Writer.Complete();
- 消费者循环中必须检查
WaitToReadAsync()返回值:while (await channel.Reader.WaitToReadAsync()) { /* ... */ } // 循环退出即表示 channel 关闭且无更多数据 - 若需等待所有消费者完成,不能只等
channel.Reader.Completion,而应单独跟踪Task实例并await Task.WhenAll(...)
真正难处理的是跨多层嵌套的取消和超时——Channel 本身不持有 CancellationToken,所有 WriteAsync/ReadAsync 调用都得显式传入,漏一个就可能卡死。这点比 Go 的 channel 更易出错。
# ai
# 的是
# 会把
# 很多人
# 多个
# 已有
# 才知道
# 不支持
# 再用
# ui
# http
# go
# 循环
# 并发
# 区别
# c#
# .net
# 线程
# 栈
# 异步
# 死锁
# 无锁
# channel
# 返回值
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- Win11怎么设置开机密码_Windows11账户
- Win10系统映像怎么恢复 Win10使用系统映像
- 如何在Golang中处理数据库事务错误_回滚和日志
- Win11怎么设置默认浏览器Chrome_Wind
- c++中的可变参数模板(variadic temp
- 如何高效删除 NumPy 二维数组中所有元素相同的
- Python随机数生成_random模块说明【指导
- Win11怎么关闭资讯和兴趣_Windows11任
- 如何使用Golang实现容器健康检查_监控和自动重
- 一文详解网站被黑客入侵挂马解决办法
- Go 中的 := 运算符:类型推导机制与使用边界详
- Win10怎样设置闹钟贪睡时间 Win10闹钟贪睡
- Win11怎么设置快速访问_Windows11文件
- phpstudy本地环境mysql忘记密码_重置m
- Windows10如何查看保存的WiFi密码_Wi
- c# F# 的 MailboxProcessor
- Win11蓝牙开关不见了怎么办_Win11蓝牙驱动
- Python爬虫项目实战教程_Scrapy抓取与存
- mac怎么看硬盘大小_MAC查看磁盘存储空间与文件
- 如何在 Go 中正确反序列化多个同级 XML 元素
- Win10路由器怎么隐藏ssid Win10隐藏w
- Windows10电脑怎么设置自动连接WiFi_W
- Mac怎么开启“任何来源”_Mac安装未签名应用的
- Laravel 查询 JSON 列:高效筛选包含数
- Win11怎么设置快速访问主页_Windows11
- PHP 中如何在函数内持久化修改引用变量的指向
- Win11怎么开启HDR模式_Windows 11
- c# 如何深拷贝和浅拷贝
- php485返回空数组怎么回事_php485数据接
- VSC怎么在PHP中调试MySQL_数据库交互排查
- php485在macos下怎么配置_php485
- 如何在Golang中使用container/hea
- Windows10如何彻底关闭自动更新_Win10
- Win11怎么设置默认PDF阅读器 Win11修改
- c++怎么实现高并发下的无锁队列_c++ std:
- Win11任务栏怎么放到顶部_Win11修改任务栏
- php查询数据怎么导出csv_查询结果转csv文件
- Win11怎么关闭贴靠布局_Win11禁用窗口最大
- Windows11怎么自定义任务栏_Windows
- C++中的std::shared_from_thi
- php8.4匿名类怎么用_php8.4匿名类创建与
- Win11怎么设置默认邮件客户端 Win11修改M
- 如何使用正则表达式批量替换重复的星号-短横模式为固
- Win11怎么设置单手模式_Win11触控键盘布局
- 如何用::实现工具类方法调用_php静态工具类设计
- 如何高效识别并拦截拼接式恶意域名 spam
- Win11怎么忘记WiFi网络_Win11删除已保
- 如何用正则表达式精确匹配“start”到“end”
- Windows 10怎么录屏_Windows 10
- 如何使用Golang defer优化性能_减少不必


QQ客服