ParallelStart
Category: Common Steps
Namespace: CMS.Plugin.FlowManagement.Abstractions.FlowBusiness.Activitys
Base Class: ParallelStartActivity (SYC.Flow.Kernel)
Module: FlowManagement.Abstractions
Overview
ParallelStart is a special node used to start parallel flows. When the flow executes to this node, it creates a parallel branch point, allowing multiple flow branches to execute simultaneously. It inherits from SYC.Flow.Kernel.ParallelStartActivity, providing initialization and level code management for parallel flows.
Parallel flows are suitable for scenarios requiring simultaneous execution of multiple independent tasks, such as multiple concurrent checks, parallel processing of multiple workpieces, etc. The ParallelStart node automatically manages level codes for parallel branches, ensuring each branch has a unique identifier.
Business Scenarios
Applicable Scenarios
- Parallel Detection: Simultaneously perform multiple independent quality checks on a product
- Parallel Processing: Simultaneously process multiple workpieces or batches
- Parallel Call: Simultaneously call multiple external APIs or services
- Parallel Printing: Simultaneously print multiple labels or reports
- Branch Flow: Create multiple parallel executing flow branches based on conditions
Role in System
ParallelStart plays the role of a parallel flow controller in the LMES flow system:
- Marks the start point of parallel flow
- Assigns a unique level code for each parallel branch
- Creates work item records for parallel flows
- Manages context information for parallel flows
- Collaborates with ParallelEnd to achieve complete parallel flow control
Collaboration with Other Nodes
- ParallelEnd: Must be paired, ParallelEnd is responsible for converging parallel branches
- BusinessActivity: Business nodes in parallel branches, execute business logic normally
- Transition: Multiple transition lines originate from ParallelStart to form parallel branches
Configuration Description
Basic Configuration
| Property Name | Type | Required | Default | Description |
|---|---|---|---|---|
| ExtendedProperty | BusinessPropertyCollection | No | Empty Collection | Step extended properties |
Configuration Details
ExtendedProperty
Description: Extended property collection, allowing custom properties to be added to the node, which can be written to flow context for use by parallel branches.
Value Range: BusinessPropertyCollection object
Notes:
- Extended properties will be written to flow context when parallel flow starts
- All parallel branches can access these extended properties
- Values of extended properties will automatically have "_Value" suffix added as flow context keys
Flow Context
Input Parameters
| Parameter Name | Type | Description |
|---|---|---|
| ApplicationData | object | Application data (such as ProcessModel) |
| Upstream Node Data | any | Business data written by upstream nodes |
Output Parameters
| Parameter Name | Type | Description |
|---|---|---|
| ParalleInfo | IDictionary | Parallel flow info (default System) |
| LevelCode | string | Level code of current parallel branch |
| Extended Property Value | any | Property values configured in ExtendedProperty |
Data Flow Description
- Level Code Management: ParallelStart generates a unique level code (LevelCode) for each parallel branch
- Context Inheritance: Parallel branches inherit flow context data from before ParallelStart
- Data Isolation: Each parallel branch has independent work item (FlowItem), but shares flow context
- Key Info Passing: KeyInfo and KeyLabel are passed from current task to parallel branches
Business Logic Description
Processing Flow
The execution flow of ParallelStart is as follows:
-
EnterAsync: Enter parallel flow start node
- Get current task information (CurrentTask)
- Create new work item (FlowItem)
- Set work item properties:
- StartTime and FinishTime: Current time
- PartName: Fixed as "System"
- TaskStat: FlowItemStatus.AutoFinished
- AutoFinish: 1 (Auto finish flag)
- EnableCalculate: 1 (Participate in calculation)
- LevelCode: Current level code
- KeyInfo and KeyLabel: Inherited from current task
- TaskName: Node name
- Add work item to flow instance
- Update current work item ID
- Record entry log
-
Parallel Branch Creation:
- Flow engine creates multiple parallel branches based on outgoing transitions from ParallelStart
- Each branch gets a unique level code
-
ExitAsync: Exit parallel flow start node
- Calculate execution time
- Record exit log
Flow Chart
[Upstream Node]
↓
[ParallelStart]
├─→ [Parallel Branch 1] → [Business Node 1] ┐
├─→ [Parallel Branch 2] → [Business Node 2] ├→ [ParallelEnd]
└─→ [Parallel Branch 3] → [Business Node 3] ┘
Level Code Mechanism
Level Code (LevelCode) is the core mechanism of parallel flow:
- Format: Hierarchical structure like "00-1:01-1:02-1:"
- Function: Uniquely identify each parallel branch and subflow
- Generation: Generated by ParallelStart based on current task's level code
- Usage: ParallelEnd uses level code to identify and converge parallel branches
Dependency Services
| Service Interface | Purpose | Description |
|---|---|---|
| Flow | Flow Instance | Access flow context and work items |
| Flow.Logger | Logger | Record execution logs of parallel flow |
Exception Handling
ParallelStart node itself does not throw business exceptions, but note:
- Null reference exception if flow instance is null
- Level code generation failure may affect correct execution of parallel flow
- Work item addition failure affects flow tracking
Logging
ParallelStart records the following key logs:
- Enter Node:
Enter, TaskID={taskId} - Exit Node:
Exit, Time Consumption={milliseconds}ms
Usage Examples
Basic Example
{
"Name": "Parallel Detection Flow",
"Activities": [
{
"Type": "BusinessActivity",
"Name": "Prepare Data",
"Alias": "PrepareData"
},
{
"Type": "ParallelStart",
"Name": "Start Parallel Detection",
"Alias": "ParallelStart1"
},
{
"Type": "BusinessActivity",
"Name": "Visual Check",
"Alias": "AppearanceCheck"
},
{
"Type": "BusinessActivity",
"Name": "Dimension Check",
"Alias": "DimensionCheck"
},
{
"Type": "BusinessActivity",
"Name": "Function Check",
"Alias": "FunctionCheck"
},
{
"Type": "ParallelEnd",
"Name": "End Parallel Detection",
"Alias": "ParallelEnd1",
"WaitMode": "WaitAll"
},
{
"Type": "BusinessActivity",
"Name": "Summarize Results",
"Alias": "SummaryResult"
}
],
"Transitions": [
{
"From": "PrepareData",
"To": "ParallelStart1"
},
{
"From": "ParallelStart1",
"To": "AppearanceCheck"
},
{
"From": "ParallelStart1",
"To": "DimensionCheck"
},
{
"From": "ParallelStart1",
"To": "FunctionCheck"
},
{
"From": "AppearanceCheck",
"To": "ParallelEnd1"
},
{
"From": "DimensionCheck",
"To": "ParallelEnd1"
},
{
"From": "FunctionCheck",
"To": "ParallelEnd1"
},
{
"From": "ParallelEnd1",
"To": "SummaryResult"
}
]
}
Advanced Example: With Extended Properties
{
"Type": "ParallelStart",
"Name": "Start Parallel Print",
"Alias": "ParallelPrintStart",
"ExtendedProperty": [
{
"Name": "PrintCount",
"Value": "3",
"WriteIntoDataItems": true
},
{
"Name": "PrinterGroup",
"Value": "LabelPrinters",
"WriteIntoDataItems": true
}
]
}
Complete Flow Example: Parallel API Call
{
"Name": "Parallel API Call Flow",
"Activities": [
{
"Type": "BusinessActivity",
"Name": "Get Order Info",
"Alias": "GetOrder"
},
{
"Type": "ParallelStart",
"Name": "Start Parallel Call",
"Alias": "ParallelAPIStart"
},
{
"Type": "ApiCallActivity",
"Name": "Query Inventory",
"Alias": "CheckInventory",
"ApiId": "InventoryAPI"
},
{
"Type": "ApiCallActivity",
"Name": "Query Price",
"Alias": "CheckPrice",
"ApiId": "PriceAPI"
},
{
"Type": "ApiCallActivity",
"Name": "Query Logistics",
"Alias": "CheckLogistics",
"ApiId": "LogisticsAPI"
},
{
"Type": "ParallelEnd",
"Name": "End Parallel Call",
"Alias": "ParallelAPIEnd",
"WaitMode": "WaitAll"
},
{
"Type": "BusinessActivity",
"Name": "Process Results",
"Alias": "ProcessResults"
}
],
"Transitions": [
{
"From": "GetOrder",
"To": "ParallelAPIStart"
},
{
"From": "ParallelAPIStart",
"To": "CheckInventory"
},
{
"From": "ParallelAPIStart",
"To": "CheckPrice"
},
{
"From": "ParallelAPIStart",
"To": "CheckLogistics"
},
{
"From": "CheckInventory",
"To": "ParallelAPIEnd"
},
{
"From": "CheckPrice",
"To": "ParallelAPIEnd"
},
{
"From": "CheckLogistics",
"To": "ParallelAPIEnd"
},
{
"From": "ParallelAPIEnd",
"To": "ProcessResults"
}
]
}
Extension Development Guide
Inheritance Hierarchy
Activity (SYC.Flow.Kernel)
└── ParallelStartActivity (SYC.Flow.Kernel)
└── ParallelStart
Overridable Methods
| Method Name | Purpose | When to Override |
|---|---|---|
| EnterAsync | Executed when entering node | Need custom parallel flow initialization logic |
| ExitAsync | Executed when exiting node | Need custom processing after parallel flow start |
| ParalleInfo | Parallel flow info | Need custom parallel branch identification info |
| LevelCode | Level code | Need custom level code generation logic |
Custom Parallel Start Node Example
[Serializable]
[Design("Custom Parallel Start", "Parallel start node with custom logic", Sort = 1)]
[Category("Custom")]
public class CustomParallelStart : ParallelStart
{
[Design("Parallel Branch Count", "Number of parallel branches to create", Sort = 1)]
[Category("Configuration")]
[DataMember]
public int BranchCount { get; set; } = 3;
public override IDictionary ParalleInfo
{
get
{
var info = new SortedList();
for (int i = 0; i < BranchCount; i++)
{
info.Add($"Branch{i + 1}", $"Branch{i + 1}");
}
return info;
}
}
public override async Task EnterAsync(ProcessflowEventArgs e)
{
// Custom initialization logic
Flow.Logger.LogMessage($"Creating {BranchCount} parallel branches", Name);
// Prepare data for each branch
for (int i = 0; i < BranchCount; i++)
{
Flow.DataItems[$"Branch{i}_Data"] = $"Data for Branch{i + 1}";
}
await base.EnterAsync(e);
}
}
Registration and Integration
-
Compile Node:
- Inherit ParallelStart class
- Add Design and Category attributes
- Compile to DLL
-
Deploy Node:
- Place DLL in LMES plugin directory
- Restart LMES service
-
Use Node:
- Select custom parallel start node in flow designer
- Configure node properties
- Connect parallel branches
Notes
- ⚠️ Must Pair: ParallelStart must be paired with ParallelEnd, otherwise flow cannot execute correctly
- ⚠️ Branch Count: Number of parallel branches is determined by number of transition lines outgoing from ParallelStart
- ⚠️ Level Code: Do not manually modify level code, managed automatically by system
- ⚠️ Work Item: ParallelStart automatically creates work item with AutoFinished status
- ⚠️ Data Sharing: All parallel branches share the same flow context, beware of data conflicts
- ⚠️ Execution Order: Execution order of parallel branches is indeterminate, do not rely on specific order
- 💡 Best Practices:
- Prepare data needed by all parallel branches before ParallelStart
- Use extended properties to pass common configuration to all branches
- Use different flow context keys for each branch to avoid data overwrite
- Set appropriate waiting mode (WaitAll or WaitOne) for ParallelEnd
Related Nodes
- ParallelEnd: Parallel flow end node, must be paired
- BusinessActivity: Business node base class in parallel branches
- SubflowActivity: Subflow node, also supports parallel execution
FAQ
Q1: Must ParallelStart and ParallelEnd be paired?
A: Yes, they must be paired. ParallelStart marks the start of a parallel flow, ParallelEnd is responsible for converging all parallel branches. If ParallelEnd is missing, the flow cannot end correctly.
Q2: How to control the number of parallel branches?
A: The number of parallel branches is determined by the number of Transition lines outgoing from ParallelStart. One transition line corresponds to one parallel branch.
Q3: How to share data between parallel branches?
A: All parallel branches share the same flow context (Flow.DataItems). Data can be passed via flow context, but note:
- Use different key names to avoid data overwrite
- Consider using thread-safe data structures
- Read results of all branches after ParallelEnd
Q4: What is the execution order of parallel branches?
A: The execution order of parallel branches is indeterminate, decided by the flow engine's scheduling. Do not rely on specific execution order. If sequential execution is needed, use serial flow.
Q5: How to handle exceptions in parallel branches?
A: Each parallel branch handles exceptions independently. If a branch throws an exception:
- That branch stops execution
- Other branches continue execution
- ParallelEnd's waiting mode determines handling:
- WaitAll: Wait for all branches to complete (including exception branches)
- WaitOne: Continue as soon as one branch completes
Update History
| Date | Version | Description |
|---|---|---|
| 2025-11-28 | 1.0 | Initial Release |
Last updated: 2025-11-28