事件驱动架构...无限循环
我有一个事件驱动的架构,其中 A 正在等待 B 的更改,B 正在等待 C 的更改,C 正在等待 A 的更改,形成一个循环。
现在,如果 B 发生变化,则 A 会向 C 触发一个事件,C 触发事件,B 触发事件,A 触发事件,A 触发事件,C 触发事件……无穷无尽。
我现在可以更改我的程序以不包含此循环,但我担心稍后我可能会将自己陷入一个我无法做到的角落。在设计基于事件的系统时如何防止此类事情发生?
I have an event driven architecture where A is waiting for a change from B and B is waiting for a change from C and C is waiting for a change from A, forming a cycle.
Now, if B changes, then A fires an event to C, which fires to B, which fires to A, which fires to C...ad infinitum.
I can change my program right now to not contain this cycle, but I am concerned I may put myself into a corner at a later time where I cannot. How does one keep such things from happening when designing event based systems?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
这里的每个人似乎都说循环依赖是不好的。这在某种意义上是正确的,我试图不惜一切代价避免静态循环依赖。您可以通过控制反转来实现此目的,如本博客中所述:http: //blog.schauderhaft.de/2011/07/17/writing-dependency-cylces/
但是您所描述的不一定是静态循环依赖,而是运行时的依赖。我不完全确定,但我认为在运行时避免循环依赖或多或少是不可能的。但当然这不应该导致无限循环。为了解决这些问题,我看到了两个半选项
首先是黑客
确保由另一个事件触发的每个事件都有对原始事件的引用(或有关它的基本信息,如 ID)。当你处理一个事件时,请确保它不是由你自己发起的。
优点:易于实施;绝对防止递归
黑客的另一半
如果您正在运行同步,您可以在之前设置一个标志
fireingEvent
并在之后重置它。设置firingEvent
时忽略传入的事件。优点:更容易实施;在单线程中运行时绝对防止递归
语义丰富的解决方案
我确信 A 在某些外部触发器上触发的事件和 A 由于 C 触发而触发的事件实际上是两个不同的事件,或者都是三个事件实际上只是一个可能来自尚未确定的来源 D. 或类似的东西。如果没有信息,就无法判断 A、B 和 C 是什么以及它们正在触发什么事件。如果你找到适当的事件,循环就会消失。
优点:设计会更简洁并包含更多信息。
Everybody here seems to say that cyclic dependencies are bad. This correct in a sense and I try to avoid static cyclic dependencies at almost all costs. You can do so with inversion of control as sketched in this blog: http://blog.schauderhaft.de/2011/07/17/breaking-dependency-cylces/
But what you describe is not necessary a static cyclic dependency, but one at runtime. I'm not completely sure, but I think it is more or less impossible to avoid cyclic dependencies at runtime. But of course this should not result in endless loops. To fix those I see two and a half options
First the hack
Make sure each event triggered by another event has a reference to the original event (or essential information about it like an id). When ever you process an event make sure it doesn't originate from your self.
Pro: easy to implement; prevents recursion absolutely
The other half of the hack
If you are running synchronous you can set a flag
firingEvent
before and resetting it after. Ignore events comming in whilefiringEvent
is set.Pro: even easier to implement; prevents recursion absolutely when running in a single thread
Semantic rich solution
I'm convinced that the event that A fires on some outside trigger and the event that A fires because C fires are really two different events, or all three events are really just one which might come from a yet to be identified source D. Or something like that. There is no way telling without information abut what A, B and C are and what events they are firing. If you find the proper events the cycle will disappear.
Pro: The design will be cleaner and contain more information.
列出您的依赖关系。不应该有循环。循环依赖是重新组织代码的好借口。
它们还可能导致死锁,以防您需要另一个理由来避免它们。
Map out your dependencies. There should be no cycles. Cyclical dependencies are a good excuse to reorganize your code.
They can also cause deadlocks, in case you needed another reason to try to avoid them.
仅当对象状态真正发生变化时才引发事件。
在引发事件时禁止对象状态更改。
Raise event only when object state really changes.
Prohibit object state changing while event is raised.
我认为这是一个好问题。不幸的是,我自己没有完整的答案,但这篇文章有几个优点:
如何避免观察者模式中的无限循环?
我不认为答案是避免循环依赖,正如其他人所建议的那样。 (嗯,这取决于你对“循环依赖”的定义。)像 Java 这样的语言将使用接口在编译时最小化类型的循环依赖,这通常是一个好主意。例如, MVC 模式并不“依赖于”您的应用程序,它只知道具有诸如
ValueChangedListener
、ClickListener
等名称的接口。但这并不能消除运行时对象之间的循环连接时间,这可能导致事件循环。正如另一篇链接文章中提到的,UI 工具包中的某些循环会停止,因为如果控制器或模型将视图的值“设置”为等于其当前值,视图将不会触发“更改”事件。但在其他情况下,例如当您创建更复杂的数据的自定义视图时,计算当前数据和新数据的相等性可能是不可行的。
I think this is a good question. Unfortunately I don't have an full answer myself, but this post has a couple good points:
how to avoid infinite loop in observer pattern?
I don't think the answer is to avoid cyclic dependencies, as others have suggested. (Well, it depends on your definition of "cyclic dependency".) A language like Java will use interfaces to minimize cyclic dependencies of types at compile time, which is often a good idea. For example, a view class in the MVC pattern does not "depend" on your application, it only knows about interfaces with names like
ValueChangedListener
,ClickListener
, etc. But this does not eliminate cyclic connections among objects at run time, which is what can lead to event loops.As mentioned in the other linked post, some loops are stopped in UI toolkits because the view won't fire a 'changed' event if the controller or model "sets" the view's value to something equal to it's current value. But in other cases, like when you create a custom view of a more complex piece of data, it can be infeasible to compute equality of the current and new data.
循环依赖确实很糟糕。我必须先用 A、B 和 C 来写下你的帖子,然后才能对我有意义。我认为你应该摆脱它。如果您将自己置于困境,这可能比循环依赖可能遇到的问题要好得多。
您也绝对可以避免这种情况。 A、B 和 C 确实紧密耦合。我认为你需要重新考虑他们的责任。也许有一个常见的 D 元素可以减轻您的设计压力。
我想到的其他东西是架构分层。如果您可以将 A 分层放在 B 上,并要求与 B 交谈的任何人进行通信以穿过 A 并向下分层,那么您可能会更轻松。再说一次,我对你的问题不太了解,所以这些只是广泛的建议。
最后一个选择,也是我最不喜欢的,是在三个组件之间传递消息。当访问每个组件时,要求每个组件将其已看到该消息添加到消息中。然后,下一个获取消息的组件将包含有关谁看到该消息的信息。有点像登记表。但同样,最不喜欢的。先尝试别的事情。
祝你好运!
Cyclic dependencies are really bad. I had to write down your post in terms of A, B, and C before it even made sense to me. I think you should get rid of it. If you're putting yourself in a corner, it's probably a whole lot better than the problems you can run into with cyclic dependencies.
You can definitely avoid this, too. A, B, and C are really tightly coupled. I think you need to rethink their responsibilities. Perhaps there's a common D element that'll take a lot of your design-stress away.
Something else that comes to mind is architectural Layering. If you can layer A over B, and require communication by anyone speaking to B to go through A and down the layers, you might give yourself an easier time. Again, I don't know much about your problem, so these are just broad suggestions.
One final option, and my least favorite, is to pass a message between each of the three components. As each is visited, require that each component add to the message that it has seen the message. Then the next component to get the message has information about who's seen it. Kind of like a sign up sheet. But again, least favorite. Try something else first.
Good luck!