Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

合同接口(IpcContract)支持事件 #12

Open
DingpingZhang opened this issue Aug 10, 2021 · 1 comment
Open

合同接口(IpcContract)支持事件 #12

DingpingZhang opened this issue Aug 10, 2021 · 1 comment
Assignees
Labels
enhancement New feature or request

Comments

@DingpingZhang
Copy link
Member

背景:

当前的合同接口仅支持方法成员,按目前设定:Server 在同一个域内应该是唯一的,不允许启动第二个 Server 实例,以避免 Client 无法区分服务的提供者;而 Client 方则允许创建多个实例与唯一的 Server 交互,但交互方式仅为:Client 调用 Server,Server 在本次调用中应答一次 Client,是单对单的交互方式,还缺乏一种单对多的通知方式,即多播事件。

方案:

使当前的合同接口可以定义事件,事件的行为应当是:Server 发布事件后,所有 Client 实例(可能有多个)中订阅了该事件的 Handler 都应该被触发。Client 之间若想交互,仅可以通过调用 Server 方法以触发某个 Server Event 的方式进行。

实现:

该 Feature 本质上是:

  1. Server 和 Client 的角色互换,
  2. 且 Server 还需要储存多个连接到它的 Client 实例,以做到多播。

考虑到以下问题,我们并不能简单地将目前的 SenderBaseReceiverBase 进行互换:Tcp 中,同一台机器中的同一个端口仅允许被一个实例监听,如果 Client 侧也创建 ReceiverBase,那么端口号将不方便选取,选取后也还需要通知 Server 侧,实现起来比较麻烦;NamedPipe 虽然允许创建多个同名 Pipe,但多个进程中包含的同名 ServerStream 也会导致 Client 无法区分正确的连接目标。
所以,应当借助 Tcp 和 NamedPipe 的全双工能力实现反向的通知。具体的:

  1. 当 Client 第一次订阅事件时(可以在 ClientProxy 中拦截到),Client 向 Server 发送一个订阅请求;
  2. Server 处理该请求,并缓存本次连接。当 Server 中该事件被触发后,将向所有缓存中的连接推送消息;
  3. 当 Client 取消事件订阅后,并且 handlerCounter 计数器归零时,Client 向 Server 发送一个订阅请求;
  4. Server 释放相应的连接资源。
@DingpingZhang DingpingZhang added the enhancement New feature or request label Aug 10, 2021
@DingpingZhang DingpingZhang self-assigned this Aug 10, 2021
@DingpingZhang DingpingZhang mentioned this issue Aug 14, 2021
4 tasks
@DingpingZhang DingpingZhang changed the title 合同接口(IpcContract)支持事件及更多的成员类型 合同接口(IpcContract)支持事件 Nov 20, 2021
@DingpingZhang
Copy link
Member Author

关于事件的实现,有两种方案:同步和异步。

1. 同步

即当 Client 调用 Raise Event 方法时,该方法被阻塞,直到所有订阅了该事件的 Handler (可能存在于多个进程内)全部执行完毕,才会返回。

优点:

  • 与 C# 原生事件的行为相符,不会对用户造成额外的学习成本。

缺点:

  • 为保证所有 Handlers 均执行完毕后返回,需要在执行 Handlers 时加锁。在某些 CPU 上(i7-8700K)Lock contention 会造成巨大的性能损耗(CPU 占用率高达 70+%),但在另一些 CPU 上则没有性能问题(i5-8300H、i7-1185G7)。(猜想:i7-8700K 核心数更多,开的线程更多,造成 Lock contention 更频繁?)
  • 多个进程彼此之间的关联性是很弱的,且事件没有返回值,所以 Raise Event 的进程本就无法收到执行的结果,只能得到是否执行完成的反馈(该反馈的用处似乎不大?),故没有充分的理由支持:一个进程需要等待其它进程中的 Handler 执行完毕。
  • 进一步地,若其它进程中的 Handler 实现不规范,如:死锁、死循环、阻塞不返回,则会导致 Raise Event 的进程被卡死,导致一个进程的行为强依赖于其它进程,这在大多数情况下是不被期待的。

2. 异步

即当 Raise Event 时,仅仅只是把 EventArgs Push 到消息队列后就立即返回,不管后续执行结果。
注意:Subscribe/Unsubscribe 还是同步订阅,因为它们需要一个确定的执行成功的结果。

优点:

  • 可以不使用 Lock,没有性能问题。
  • 多个订阅进程之间彼此隔离,互不影响。
  • 用户可直接在 UI 线程中调用,不阻塞 UI,不需要自己编写额外代码实现生产-消费模式。

缺点:

  • 与 C# 原生事件行为不一致,用户会有理解障碍,可能写出不被预期的代码。(多进程耦合也会产生不预期行为)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant