源码分析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 方法运行的机会。

 

posted @   牛有肉  阅读(1165)  评论(0编辑  收藏  举报
编辑推荐:
· 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语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示