源码分析socketChannel,为什么register前需要调用selector的weakup方法
直接上源码,查看 register 的实现。我们查看 register 的实现会直接跟进抽象类 SelectableChannel 中:
调用了本身的另一个 register 方法:
该实现为抽象方法,我们直接向下查找 SelectableChannel 子类,查看其实现。因为 SocketChannel 为 ServerSocketChannel 的 accept 方法返回的,跟进 accept 方法,查看其返回了哪个实现类的实例:
可以看到,accept 方法返回的是 SocketChannelImpl 的实例,直接跟进 SocketChannelImpl ,发现并没有 register 方法的实现。说明 register 方法在其某个父类中已经被实现了,我们向上寻找最近的实现了 register 方法的父类,直接查看 UML 图:
可以看到,距离 SocketChannel 最近的持有 register 方法的父类为 AbstractSelectableChannel ,跟进去:
findKey 为 selector 中 key 已存在的情况,key 不存在时,依靠调用 AbstractSelector 的 register 方法获取 key。可以看出,selector.register(channel) 比较符合通常的逻辑,而这里用 channel 去 register( selector ) 本身在新获取 key 时也是这样实现的。暴露给我们的方法为 channel 去 register( selector ) 的原因在这里也很明显了。channel 在设计时是可以对应多个 selector 的,也就是说同一个 channel 可以在多个 selector 上注册自己感兴趣的事件。因此每次在注册事件时,register 方法会检查在该 selector 上,本 channel 是否已经注册过了,如果注册过了,直接通过 findKey 方法返回,否则才去注册。所以说在一个 selector 上,一个 channel 只能注册一次。
调用 weakup 方法的原因还没有找到,继续到 AbstractSelector 跟进。与上面的过程一致,发现 register 是在 SelectorImpl 中实现的:
而 select 方法的实现基于 lockAndDoSelect 方法,他们共用了 publicKeys 这把锁:
在 lockAndDoSelect 方法中,用 publicKeys 锁住了一个抽象方法 doSelect,doSelect 的实现不在解析,只需要知道这是一个阻塞方法,阻塞等待事件发生。
所以如果没有事件发生,锁 publicKeys 会一直被 select 方法霸占,register 方法无法获取锁会一直等待锁的释放。所以如果在调用 register 前后不调用 weakup 方法,会发生既检查不到死锁,程序又卡死在 register 方法无法向下运行的情况。
weakup 方法做的就是向事件通道写入一个字节,将 doSelect 从阻塞中唤醒,从而继续向下运行释放 publicKeys 锁,给 register 方法运行的机会。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构