Verse Wiki — 写给 UE6 时代的 Verse 互动手册
拓展 · EXTRA

sync 的结果是 tuple:并发世界的多返回值

tuple 在第 14 课的出场是「函数多返回值」,但它最漂亮的一次登场要等到并发章节:并发原语 sync 同时跑多个异步任务、等它们全部完成,把各自的结果按书写顺序打包成一个 tuple 交还给你。本页提前剧透这场联动。

tuple 在第 14 课的出场是「一个函数吐好几个值」,但它最漂亮的一次登场要等到并发章节:并发指令 sync 让好几件事同时开跑、等它们全部干完,再把各自的结果按你写代码的先后顺序打成一个 tuple 交还给你。本页提前剧透这场联动。

一、sync:等所有人都到齐再发车

后面的并发章节会正式介绍 Verse 的一组并发表达式:spawnracesyncbranch……这里只提前认识一位:sync。它的规则一句话就能说完——块里的几个异步表达式同时开跑,sync 等到它们全部完成才继续往下走。像一场双人副本:两名玩家各刷各的任务,结算界面要等两个人都通关才弹出来。

后面的并发章节会正式介绍 Verse 的一组「同时做好几件事」的指令:spawnracesyncbranch……这里只提前认识一位:sync。它的规则一句话就能说完——块里的几件事同时开跑,sync 等到它们全部干完才继续往下走。像一场双人副本:两名玩家各刷各的任务,结算界面要等两个人都通关才弹出来。(在蓝图里,你可以想成同时点亮好几条执行线,sync 就是那个「等全部到齐才放行」的汇合点。)

问题来了:两个任务各有各的结果,一个可能产出 string,另一个可能产出 int——sync 作为一个表达式,该「返回」什么?数量固定(几个任务编译期就写死了)、类型可能各不相同、要按位置一一对应……把这三个条件念出来,你会发现这就是第 14 课那张选型表里 tuple 的精确画像。没错:sync 的结果就是一个 tuple,Epic 开发者社区还有一篇专门的教程《The Tuple Result of Sync in Verse》讲这件事。

问题来了:两件事各有各的结果,一个可能产出一段文字(string),另一个可能产出一个整数(int)——sync 整体作为一个值,该「交回」什么?数量固定(几件事在 Compile 时就写死了)、类型可能各不相同、还要按位置一一对应……把这三个条件念出来,你会发现这正是第 14 课那张选型表里 tuple 的精确画像。没错:sync 的结果就是一个 tuple,Epic 开发者社区还有一篇专门的教程《The Tuple Result of Sync in Verse》讲这件事。

二、按书写顺序打包,不看完成先后

看一个双人跑圈的最小示例。RunLap 是个异步函数(挂 <suspends>,里面有 Sleep),跑完把选手名字作为结果交出:

看一个双人跑圈的最小示例。RunLap 是个会「等一会儿」的异步函数(挂了 <suspends>,里面有 Sleep——相当于蓝图里带小钟表的 Delay 节点),跑完把选手名字作为结果交出:

relay_device.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }

relay_device := class(creative_device):

    RunLap(Name:string, Seconds:float)<suspends>:string =
        Sleep(Seconds)
        Print("{Name} 冲线了")
        Name

    OnBegin<override>()<suspends>:void =
        # sync:两条跑道同时开跑,等两人都到终点
        Results := sync:
            RunLap("小红", 2.0)
            RunLap("小蓝", 3.0)
        # 结果按书写顺序打包成 tuple:(小红的结果, 小蓝的结果)
        First := Results(0)
        Second := Results(1)
        Print("成绩单:{First} 与 {Second} 都完赛")

读图翻译:RunLap 是一段「先 Delay 几秒、再打印『谁冲线了』、最后把名字交出去」的执行线。OnBegin(游戏开场,相当于 Event BeginPlay)里,sync: 底下缩进的两行——小红和小蓝——被同时点亮、各跑各的;sync 是那个「等两人都到终点才放行」的汇合点。两人的结果被打成一个两格包裹 Results,Results(0) 取第一格(小红)、Results(1) 取第二格(小蓝),最后一起打印成绩单。

关键细节在「顺序」二字:小红 2 秒完赛,小蓝 3 秒,完成时间有先有后,但 Results(0) 永远是书写顺序中第一个表达式(小红那行)的结果,Results(1) 永远是第二个。打包顺序由代码文本决定,与谁先跑完毫无关系——这让结果的类型和含义在编译期就完全确定,你可以放心地按位置取值,不用担心「今天 0 号位是小红、明天变成小蓝」的随机惊吓。

关键细节在「顺序」二字:小红 2 秒完赛,小蓝 3 秒,完成时间有先有后,但 Results(0) 永远是你代码里写在第一位那行(小红那行)的结果,Results(1) 永远是第二位。打包顺序由代码文本的先后决定,跟谁先跑完毫无关系——这让结果的类型和含义在 Compile 时就完全钉死,你可以放心按格子序号取值,不用担心「今天 0 号位是小红、明天变成小蓝」的随机惊吓。

对照第 14 课的知识复个盘:tuple 的下标必须是编译期常量、每个位置的类型独立确定、长度定死不可变——放在 sync 身上,每一条都严丝合缝。任务数量是写死的几行,结果数量就是几格;每行表达式的类型编译期已知,对应格子的类型也就已知;Results(0) 用不可失败的圆括号取值,一次并发协作的成果安安稳稳落袋。顺带一提,sync 的兄弟 race 走的是另一条路:几个任务赛跑,只取最先完成者的结果,其余淘汰——一个要「全部到齐的 tuple」,一个要「第一名的单值」,两种并发策略对应两种返回形状,这个对比留到并发章节细讲。

对照第 14 课的知识复个盘:tuple 的格子序号必须是写死的固定数字、每格的类型各自独立确定、格数定死不可变——放到 sync 身上,每一条都严丝合缝。任务写了几行,结果就是几格;每行结果的类型在 Compile 时已知,对应格子的类型也就已知;Results(0) 用绝不会扑空的圆括号取值,一次并发协作的成果安安稳稳落袋。顺带一提,sync 的兄弟 race 走的是另一条路:几件事赛跑,只取最先完成那个的结果,其余当场淘汰——一个要「全部到齐、打成一包的 tuple」,一个只要「第一名的那一个值」,两种并发玩法对应两种结果形状,这个对比留到并发章节细讲。

上面的示例里,小红 2 秒完赛、小蓝 3 秒完赛,Results(0) 拿到的是谁的结果?

来源与延伸

本文整理自 Epic 开发者社区教程,sync 更完整的用法请以官方文档与本站并发章节为准: