通俗理解 grasshopper 触发更新 / 多线程处理

掉进了 grasshopper 的坑真是一把辛酸泪。

下面是 grasshopper 平台上讨论的一个问题:

http://www.grasshopper3d.com/forum/topics/triggering-solution-refresh

大牛 David Rutten 的一段评论,我翻译如下,仅供交流:

当然这是2011年的事情了,后续更新我会补上!!



1. ExpireSolution(True) vs. ExpireSolution(False) vs. NewSolution(True) vs. NewSolution(False)

当你结束调用的时候,该调用哪一个或哪一些方法?这基本上取决于你希望这些事件发生的频率有多高,以及你是想更新单个组件还是一次性更新多个组件。这几个方法分别是这样做的:

ExpireSolution(True):你调用这个方法时所在的组件以及依赖于它的所有组件,会被设置“终止”标志,基本上等于它的下游。当这些标志都设置完成后,它会调用 GH_Document.NewSolution(False)。

ExpireSolution(False):几乎同上,除了它不会通知文件去重算自身。如果你想在启动一次新的计算过程之前终止掉所有组件,那么就用这个方法。

NewSolution(True):这会设置文件中所有组件的“终止”标志,并且重算一切,这是地毯式轰炸,你最好别用。

NewSolution(False):这会重算那些被设置了“终止”标志的对象。

总结一下,在以下这些场景中你该这样去用:

  • 一个对象有改变,我们想立即重算它(比如用户拖动一个滑块,或者连接了一条线)。调用 ExpireSolution(True)
  • 由于某些场外因素,一票对象同时(或几乎同时)终止了。例如,你想挂载多个硬件控制器或传感器并且他们一直在触发更新。在所有受影响的组件上调用 ExpireSolution(False),然后调用一次 NewSolution(False)
  • 一个大事件发生了但是你并不知道发生了什么。调用 NewSolution(True),因为你并没有足够的信息去阻止缓存数据被破坏。

grasshopper 没有足够智能去推迟计算过程,直到它确定没有 ExpireObject() 的调用进来了。如果你让他去重算,它就会立即重算。如果你需要智能地延迟,你需要自行编写代码。

2. 基于线程的应用 vs. 不基于线程的应用

grasshopper 是一个不基于线程的应用,顾名思义,任何框架应用的接口部分都是单线程的。这在当你接收到另外一个线程上的外部事件时就会出现问题,有可能是硬件控制信号,有可能是传感器数据。

实际情况是这样的:

由于某些原因,grasshopper 文件被终止了,紧接着 NewSolution() 方法会被调用以期修正这个未知的错误。首先它会依次查找文件中的组件,每当发现一个组件的“终止”标志为 true,就会告诉它重新计算。如果一个组件所依赖的组件没有计算完成(‘solved’),它是没办法重新计算的。因此可能会导致整个文件的重算冲击波。最后,当所有输入可用,这个组件终于重算了之后,将把控制权交还给文件求解器。

文件求解器现在继续寻找它的“已终止”的组件。它可能遇到的是上述这种情况,也可能本来只有一个终止的组件,也可能找到的那个组件已经让其他终止组件重算了,如此一来,文件求解器就没什么事干了。

当文件求解器对所有对象都检查过了,没有“终止”的组件的时候,它会调用 grasshopper canvas 和 rhino viewports 进行重绘。

这就是一个正确的、单线程的求解过程。如果有两个或以上的线程在同一个文件中执行,简直打开了地狱大门。

让我们来设想一下,文件求解器正在解决已终止对象的问题,这时候另一个线程的外部事件发生了,这个事件马上就被相应的组件响应处理了。这个组件此时调用了 ExpireSolution(True),导致了一票组件的“终止”标志被设置。其中一些组件可能早被文件处理器在第一个线程中处理过了,即使文件处理器认为自己把他们完美处理了,然而他们还是又一次终止了。其他的组件可能由于文件处理器还没有到达他们那里,所以还是在“终止”状态。最坏的情况是,某个组件在第一个线程中已经计算出结果并成功被后续的数据流接收,在接收的过程中第二个线程把数据擦除了,这会导致一次不匹配,很有可能会崩溃。

grasshopper 核心算法并不是线程安全的,如果你调用了不同线程的方法,你会陷入崩溃。

你需要做的是从你的第二个线程里面“调用”你的第一个线程,这样第一个线程就可以在响应你的请求之前把它正在做的事做完。在你用 GUI 写一个多线程应用的时候,调用是一个非常重要的概念,网上有很多资源解释它是如何工作的,下面是其中的2篇:

http://weblogs.asp.net/justin_rogers/pages/126345.aspx

http://www.yoda.arachsys.com/csharp/threads/winforms.shtml



原文:

Hi Eric,

there‘s multiple issues here, I‘ll address them in separate posts.

1) ExpireSolution(True) vs. ExpireSolution(False) vs. NewSolution(True) vs. NewSolution(False)

Which method(s) you end up calling depends a lot on how often you expect to get these events and whether or not you want to update a single or multiple components at the same time. This is what the methods do respectively:

ExpireSolution(True): It sets the ‘expired‘ flag on the component you call this method on and all objects that depend on that component. Basically everything downstream of it. When it‘s done setting all these ‘expired‘ flags, it will place a call to GH_Document.NewSolution(False).

ExpireSolution(False): Does the same as the previous one, except it doesn‘t tell the document to re-solve itself. Use this method if you want to expire a bunch of different components before attempting a new solution.

NewSolution(True): This will set the ‘expired‘ flags of all components inside the document and resolves everything. This is carpet bombing, rarely should you use this method.

NewSolution(False): This will only resolve those objects that have their ‘expired‘ flags set.

So, to recap, given the following cases you should be using the associated approach:

- A single object was changed and we want to immediately resolve (for example when a user drags a number slider, or connects a wire). Use ExpireSolution(True).

- A bunch of objects are expired simultaneously (or nearly simultaneously) by some sort of massive off-site event. For example if you hook up multiple hardware controls or sensors and they‘re all firing updates all the time. Use ExpireSolution(False) on all the affected objects, then place a single call to NewSolution(False).

- Something massive has happened and you have no idea of what exactly it was. Use NewSolution(True), as you don‘t have enough information to limit the devastation of cached data.

Grasshopper is not smart enough to post-pone solutions briefly until it decided that no more calls to ExpireObject() are coming in. If you tell it to recompute, it will immediately recompute. If you need delayed smarts, you‘ll need to write those yourself.

2) Threaded application vs. Non-Threaded applications.

Grasshopper is a non-threaded application and by definition, the interface portion of any winforms app is single-threaded. This poses problems when you get external events that occur on a different thread, as might well be the case for hardware controls or sensor data.

Here‘s essentially what happens:

Something expires (part of) a Grasshopper document, and the NewSolution() method is called upon to rectify this. It starts by iterating over all the objects inside the document and whenever it encounters an object which has the ‘expired‘ flag set to true, it will tell it to recompute itself. This object cannot recompute itself unless all the objects it depends on are also ‘solved‘, so it might cause a shock-wave of recomputes across the entire document. Eventually though all the inputs are available and it recomputes itself and then hands control back to the document solver.

The document solver now continues on its quest to find more ‘expired‘ objects. It might find some in which case the above story is repeated, or maybe there was only ever one expired object, or maybe the object we just found caused all other expires objects to solve themselves, leaving nothing to do for the document solver.

When the document solver has iterated over all the objects and determined that none of them are still ‘expired‘, it will place a call to redraw the Grasshopper canvas and the Rhino viewports.

That is what happens during a proper, single-threaded solution. When there‘s two (or more) threads operating on the same document, all hell breaks loose:

Let‘s assume the document solver is halfway done with its job of resolving all expired objects. At this point an external event comes in on a different thread, which is handled immediately by some component. This component places a call to ExpireSolution(True), which sets the ‘expired‘ flags of a bunch of components. Some of these components will already have been handled by the solver in the first thread, so even though the solver thinks all is fine and dandy, these components have in fact expired again. Other components might still be expired because the solver hasn‘t gotten to them yet. Worst case scenario, a component just finished solving itself on the first thread and the data inside of it is being harvested by a downstream component. While it‘s collecting this data the second thread wipes the data, causing a mismatch and probably a crash.

The Grasshopper core algorithms are not thread-safe and if you start calling methods from different threads, you will run into clashes.

What you need to do is ‘invoke‘ the first thread from your second thread. That way the first thread is allowed to finish what it‘s doing before getting around to your request. Invoking is a very important concept when you‘re writing a multi-threaded app with a GUI and there‘s plenty of online resources explaining how it works. Here‘s two:

http://weblogs.asp.net/justin_rogers/pages/126345.aspx

http://www.yoda.arachsys.com/csharp/threads/winforms.shtml

--

David Rutten

[email protected]

Poprad, Slovakia

时间: 2024-08-02 14:16:39

通俗理解 grasshopper 触发更新 / 多线程处理的相关文章

Effective Java通俗理解(持续更新)

这篇博客是Java经典书籍<Effective Java(第二版)>的读书笔记,此书共有78条关于编写高质量Java代码的建议,我会试着逐一对其进行更为通俗易懂地讲解,故此篇博客的更新大约会持续1个月左右. 第1条:考虑用静态工厂方法代替构造器 通常情况下我们会利用类的构造器对其进行实例化,这似乎毫无疑问.但“静态工厂方法”也需要引起我们的高度注意. 什么是“静态工厂方法”?这不同于设计模式中的工厂方法,我们可以理解它为“在一个类中用一个静态方法来返回这个类的实例”,例如: public st

分布式理论之一:Paxos算法的通俗理解

维基的简介:Paxos算法是莱斯利·兰伯特(Leslie Lamport,就是 LaTeX 中的"La",此人现在在微软研究院)于1990年提出的一种基于消息传递且具有高度容错特性的一致性算法. Paxos算法目前在Google的Chubby.MegaStore.Spanner等系统中得到了应用,Hadoop中的ZooKeeper也使用了Paxos算法,在上面的各个系统中,使用的算法与Lamport提出的原始Paxos并不完全一样,这个以后再慢慢分析.本博文的目的是,如何让一个小白在半

Activity生命周期的通俗理解

一般一个Activity有三种状态: 1.在屏幕上是可见的且可操作的,他是活跃或运行状态,负责响应用户操作. 2.失去焦点但仍然可见时,他处于暂停状态.也就是说未被完全遮蔽,所以该Activity仍对用户可见,但是当系统处于繁忙的时候下,有肯会杀死该Activity. 3.完全被另一个Activity覆盖时处于停止状态.也有可能被杀死. Activity生命周期中各方法的调用情况 1.onCreate(Bundle savedStatus):第一次创建时调用,只调用一次. 2.onStart()

WebGL 启动载入触发更新流程分析

太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:太阳火神的漂亮人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS.Android.Html5.Arduino.pcDuino,否则.出自本博客的文章拒绝转载或再转载.谢谢合作. requestAnimFrame(tick); 此命令是 HTML5 中新增的用于替换定时器触发更新的命令,以实现动画更新,其后台实现有一特殊之处

WebGL 启动加载触发更新流程分析

太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS.Android.Html5.Arduino.pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作. requestAnimFrame(tick); 此命令是 HTML5 中新增的用于替换定时器触发更新的命令,以实现动画更新,其后台实现有一特殊之处

网络七层协议的通俗理解

OSI七层模式简单通俗理解 这个模型学了好多次,总是记不住.今天又看了一遍,发现用历史推演的角度去看问题会更有逻辑,更好记.本文不一定严谨,可能有错漏,主要是抛砖引玉,帮助记性不好的人.总体来说,OSI模型是从底层往上层发展出来的. 这个模型推出的最开始,是是因为美国人有两台机器之间进行通信的需求. 需求1: 科学家要解决的第一个问题是,两个硬件之间怎么通信.具体就是一台发些比特流,然后另一台能收到. 于是,科学家发明了物理层: 主要定义物理设备标准,如网线的接口类型.光纤的接口类型.各种传输介

通俗理解数据库隔离机制

=========================================== 原文链接: 通俗理解数据库隔离机制   转载请注明出处! =========================================== 在理解数据库隔离机制的时候发现网上很多文章都是千篇一律,解释语言太过于标准书面化,描述的晦涩难懂,因果关系模糊.在这里将自己对隔离机制的理解描述一下,力争做到能够通过浅显的语言描述出来. 数据库隔离机制是对于多线程同时操作数据库而言的.对于单线程操作数据库不存在所谓

SSL服务器认证过程通俗理解!

理解有错误的地方,请高手指正! 1,CA中心,有一套自己的公钥和私钥,服务器用自己的私钥去生成一个自认证的证书 2,CA中心的自认证证书是有公信力的,一般被客户端所熟知,发放到每个客户端! 3,客户端需要将CA中的自认证证书加入信任列表! 4,服务器要加入CA体系,要向CA中心申请,CA中心验证了服务器的资料后,向server发放一个证书(key),里面包含了一个秘钥 5,CA发给server的证书是用CA自己的秘钥和申请者的秘钥(key)加密过的, 6,证书里面包含:申请者的身份信息.申请者公

【原创】我所理解的自动更新-客户端更新流程

创建更新线程,跟ui主线程通过message进行交互.1,去http://version.mygame.com/check.php? channelid=%d&appver=%d&resver=%d获取客户端最新版本信息.用curl获取,代码如下,至于curl的具体参数,man或者搜索引擎会告诉你答案 1 static size_t funcGetHttpText(void *ptr, size_t size, size_t nmemb, void *userdata) { 2 size_