15.3.3 语义

初始节点

InitialNode(初始节点)是一个控制节点,它充当活动执行的起始点。一个活动可以有多个初始节点。如果一个活动有多个初始节点,那么调用该活动会启动多个并发的控制流,每个初始节点一个。(其它的并发流可能始于输入活动参数节点和使能的可执行节点。)

初始节点不应该有流入活动边,这意味着当活动开始执行时初始节点总是使能的,并且每个初始节点都有一个控制token。初始节点的流出活动边必须都是控制流。初始节点上的控制token并发的提交给所有的流出控制流。

初始节点对于控制节点不能“持有”tokens这一规则是个例外,但只能关联它们的流动。如果由初始节点提交的token没有立即被接受,或者被阻塞不能移动到下游(例如受限于活动边的guard),那么它将停留在初始节点。(这语义上等价于在初始节点和它的流出边中插入一个中央缓冲节点;参见15.4.3中的中央缓冲节点的语义。)

结束节点

FinalNode(结束节点)是一个控制节点,控制流的结束。结束节点不应该有流出活动边。结束节点接受它的流入活动边交付给它的所有tokens。

有两种类型的结束节点:

  1. FlowFinalNode(流结束节点)终止一个流。流结束节点所接受的所有tokens都被销毁。它对活动中的其它流没有影响。
  2. ActivityFinalNode(活动结束节点)终止活动(或结构化活动节点,参见16.11)中的所有流。如果一个活动拥有多个活动结束节点,那么第一个接受一个token的将终止活动的执行。活动执行的终止应该销毁所有非输出活动参数节点的对象节点中持有的所有tokens,应该终止活动中同步调用的所有行为。然而,活动异步调用的行为执行不受影响。一旦活动的执行终止,活动的调用就按照15.2.3中描述的那样结束。

注意. 如果不希望放弃活动中的所有流,那么使用一个流结束节点而非活动结束节点。举个例子,如果相同的活动执行用于它的所有调用(isSingleExecution=true),那么多个tokens在一个执行中流动。此时,很可能不希望因为一个到达了一个结束节点就终止所有的流。使用一个流结束节点将简单的将到达的tokens消费掉而不终止其它流。作为另一种选择,可以为不同的调用安排单独的执行(isSingleExecution=false),这样不同调用的tokens也不会彼此影响。

Fork Nodes

ForkNode将一个流分为多个并发流。ForkNode应该只有一个流入活动边。如果流入边是控制流,那么所有的流出边也都应是控制流,如果流入边事对象流,那么所有的流出边都应是对象流。

提交给ForkNode的tokens被提交给它的所有流出活动边。如果这些交付中有一个被接受,那么交付的tokens从原始源被移除,接受者接收一份拷贝。所有其它的交付由于目标没有成功接受它们而没有被流出边所接受的仍然停留在该边待定,有可能以后被目标所接受。这些边有效地接受所提交tokens的单独拷贝,并且保持它们所接受的顺序(先进先出)。如果它们被阻塞不能流向下游,那么这对于活动边不能“持有”tokens的规则是个例外。ForkNodes的流出活动边持续持有tokens直到所有待定的交付都被它们的目标所接受。

注意. 由于活动边的guard而非活动边的目标导致的交付接受失败,那么该活动边不会接收这些tokens的拷贝。

注意. 如果在ForkNode的流出活动边上使用了guards,建模者应该确保下游的JoinNodes不依赖那些需要通过该guarded边的tokens的到达。如果这不可避免,那么应该在ForkNode和带有guard的边之间引入一个决策节点,从而如果guard失败了,tokens也可以被分流到下游的JoinNode。(参见图15.20的示例。)

Join Nodes

JoinNode同步多个流,它应该只有一条流出活动边。如果JoinNode有一条流入边是对象流,那么流出边应该是对象流,否则流出边是控制流。

JoinNode可以有一个joinSpec,它是一个值规约,用于决定何种条件下该节点将发出一个token。如果JoinNode有一个joinSpec,那么每当有新的token通过流入活动边交付给该JoinNode时,这个值规约就会被计算。这个计算不应该被任意新交付的tokens所中断,也不应该在新tokens交付给它的时候启动并发的计算。该值规约应该被计算为一个布尔值。

如果joinSpec由一个文本表达式给出,那么流入边的名称可用于指示一个布尔值来表明来自一个控制流的一个交付的出现(true)或缺失(false),或者用于指示与来自对象流(如果是的话)交付的对象token所关联的值。作为另一种选择,joinSpec可以包含一个带有一个单一布尔操作符(无操作数)名称的表达式。此时,joinSpec的值应该通过把给定的操作符应用到指示交付出现(true)或缺失(false)的布尔值上来得到结果。

如果JoinNode没有joinSpec,那么这等价于一个带有布尔操作符“and”的joinSpec表达式。也就是说,隐含缺省的joinSpec条件是在每个流入活动边上至少有一个提交的token。

如果(隐含或显式的)joinSpec计算为真,那么tokens按照如下规则提交给JoinNode的流出活动边:

  1. 如果流入边上的所有tokens都是控制tokens,那么向流出边提交一个控制token。
  2. 如果流入边上交付的tokens既有控制tokens又有对象tokens,那么只把对象tokens提交给流出边。tokens按照它们提交给JoinNode的顺序被提交给流出边。如果isCombinedDuplicate为真,那么在对象tokens提交给流出边之前,那些包含相同标识的对象的tokens被合并为一个token。

上面的规则适用于交付给JoinNode的所有tokens,包括来自相同流入边的多个tokens。

如果有tokens提交给JoinNode的流出活动边,那么再有更多(其它)的tokens被提交给流出边之前,它们应该被目标接受或拒绝通过该边(例如由于guard失败)。如果tokens被拒绝通过,它们就应该不再提交给流出边。如果JoinNode被阻塞不能向它的流出边提交tokens的话,遵循本规范的实现可以忽略不必要的joinSpec计算。

合并节点

MergeNode(合并节)点汇合多条流而不用同步。合并节点应该只有一条流出的活动边。如果合并节点的流出边是控制流,那么它的所有流入边也必须都是控制流,并且,如果流出边是对象流,那么所有的流入边都应是对象流。

合并节点的流入流上的所有tokens都提交给它的流出边。在合并节点上没有流的同步或tokens的合并(join)。

决策节点

DecisionNode(决策节点)在流出流之间进行选择。一个决策节点至少应该有一条、至多两条流入活动边,并且至少一条流出活动边。如果它有两条流入边,那么一条应该被识别为decisionInputFlow,另一条被称作主流入边。如果决策节点只有一条流入边,那么它是主流入边。如果主输入边是控制流,那么所有的流出边都应为控制流,并且,如果主输入边是对象流,那么所有的流出边都应为对象流。

决策节点接受来自主输入边上的tokens并把它们提交给它的流出边。然而,主输入边上交付的每个token最多流过一条流出边。tokens不被复制。

如果决策节点的流出边上有guards,那么对于每个到来的token它都会计算。guards以何种顺序计算没有定义,也许是并发计算的。如果主输入边是对象流,并且决策节点没有decisionInputdecisionInputFlow,那么流入的对象token中的值用于流出对象流上的guards的计算。

如果决策节点有一个decisionInputFlow,那么在来自主输入边的token被提交给流出边之前,主输入边和decisionFlow上都必须有一个token。如果决策几点没有一个decisionInput,那么decisionInputFlow上的token中所含的值用于每个流出边的guards,而不管主输入流式控制流还是对象流。

如果决策节点有一个decisionInput,那么这必须是个行为而且只有一个返回参数而没有其它输出参数。这个行为对于每个流入的(控制或对象)token都会被调用,返回的结果用于流出边的guards计算。decisionInput不应该有副作用。它不应该修改对象,但它可以,举个例子,从一个对象导航到另一个并从中获取属性值。

如果主输入边是控制流,并且该决策节点有一个decisionInput而没有decisionInputFlow,那么decisionInput不应该有输入参数。然而,如果决策节点既有decisionInput又有decisionInputFlow,那么decisionInput应该有一个in参数,并且decisionInputFlow中交付的对象token中含有的值通过这个参数传递给decisionInput(当该行为被调用的时候)。

如果主输入边是对象流,并且决策节点有decisionInput而没有decisionInputFlow,那么decisionInput应该有一个in参数并且主输入边上提交的对象token中的值通过该参数传递给decisionInput(当该行为被调用时)。然而,如果决策节点既有decisionInputFlow又有decisionInput,那么decisionInput应该有两个in参数,那么当它被调用时,主输入边上提交的token中含有的值作为第一个参数,decisionInputFlow中的对象token含有的值作为第二个参数。

当流出边的guard计算为假时,主输入边上的token不应该通过该流出边。如果有多个流出边要么没有guard、要么guard计算为真,那么流入的token最多通过其中一条边。如果正好有一个流出边接受了这个token,那么该token通过这条边并且其它的所有交付都撤销。如果多个目标同时接受这个token,那么token只能至多通过一条边,但本规范没有决定是那条边。

为了避免不确定的行为,建模者应该确保对于每个流入的token至多有一个guard计算为真。如果确保了只有一个guard计算为真,那么遵循本规范的实现只要找到一个guard计算为真的流出边即可,不必计算所有的guards。

对于只使用决策节点,可以用一个预定义的guard “else”(表示为一个带有“else”的操作符的表达式没有操作数)。该guard只有在其它流出边都不接受token的时候计算为真。

results matching ""

    No results matching ""