Skip to content

异步处理示例

本示例展示如何使用 GDSignalBus 的异步信号功能来处理耗时操作,避免阻塞主线程。

完整示例代码

gdscript
@warning_ignore_start("static_called_on_instance")
extends Node

# 异步信号示例
# 演示如何使用异步回调处理耗时操作

@onready var bus = SignalBus.get_singleton()

func _ready():
	print("\n=== Async Example Started ===")
	bus.set_debug_enabled(true)
	
	# 示例 1: 基础异步订阅
	print("Subscribing to async_event...")
	bus.subscribe_async("async_event", func(message):
		print("[异步线程] 处理消息: ", message)
		# 模拟耗时操作
		OS.delay_msec(3000)
		print("[异步线程] 处理完成")
	)
	
	# 示例 2: 带回调的异步订阅
	var async_callback = func(data):
		print("[异步线程] 开始处理数据: ", data)
		OS.delay_msec(5000)
		return "处理结果: " + str(data * 2)
	
	var complete_callback = func(result):
		print("[主线程] 处理完成,结果: ", result)
	
	var error_callback = func(error):
		print("[主线程] 处理失败: ", error)
	
	# 订阅异步信号,带完成和错误回调
	print("Subscribing to data_processing...")
	bus.subscribe_async_with_callbacks(
		"data_processing",
		async_callback,
		complete_callback,
		error_callback
	)
	print("=== Subscriptions Complete ===\n")

func _process(_delta):
	# 在主线程处理完成的异步任务
	bus.process_async_tasks()
	if Input.is_action_just_pressed("ui_up"):
		# 发射异步信号
		print("[主线程] 发射异步信号...")
		bus.emit_async("async_event", ["Hello Async!"])
		print("[主线程] 继续执行(不等待)")
	if Input.is_action_just_pressed("ui_down"):
		# 发射信号
		print("[主线程] 发射数据处理信号...")
		bus.emit_async("data_processing", [42])
		print("[主线程] 继续执行")

func _exit_tree():
	print("\n=== _exit_tree called ===")
	print("Active async tasks: ", bus.get_active_async_task_count())
	print("=== _exit_tree finished ===\n")

功能说明

1. 基础异步订阅

gdscript
bus.subscribe_async("async_event", func(message):
	print("[异步线程] 处理消息: ", message)
	# 模拟耗时操作
	OS.delay_msec(3000)
	print("[异步线程] 处理完成")
)

2. 带回调的异步订阅

gdscript
# 定义异步处理函数
var async_callback = func(data):
	print("[异步线程] 开始处理数据: ", data)
	OS.delay_msec(5000)
	return "处理结果: " + str(data * 2)

# 定义完成回调
var complete_callback = func(result):
	print("[主线程] 处理完成,结果: ", result)

# 定义错误回调
var error_callback = func(error):
	print("[主线程] 处理失败: ", error)

# 订阅带回调的异步信号
bus.subscribe_async_with_callbacks(
	"data_processing",
	async_callback,
	complete_callback,
	error_callback
)

3. 异步信号发射

gdscript
# 发射异步信号
bus.emit_async("async_event", ["Hello Async!"])
bus.emit_async("data_processing", [42])

4. 异步任务处理

gdscript
func _process(_delta):
	# 在主线程处理完成的异步任务
	bus.process_async_tasks()

控制说明

运行示例后,可以使用以下按键测试异步功能:

  • UI Up (方向键上): 发射 async_event 异步信号,触发 3 秒的异步处理
  • UI Down (方向键下): 发射 data_processing 异步信号,触发 5 秒的数据处理

输出示例

基础异步信号输出

[主线程] 发射异步信号...
[主线程] 继续执行(不等待)
[异步线程] 处理消息: Hello Async!
# 等待 3 秒后
[异步线程] 处理完成

带回调的异步信号输出

[主线程] 发射数据处理信号...
[主线程] 继续执行
[异步线程] 开始处理数据: 42
# 等待 5 秒后
[主线程] 处理完成,结果: 处理结果: 84

清理时的输出

=== _exit_tree called ===
Active async tasks: 0
=== _exit_tree finished ===

核心概念

异步处理流程

  1. 订阅阶段: 使用 subscribe_async()subscribe_async_with_callbacks() 订阅异步信号
  2. 发射阶段: 使用 emit_async() 发射异步信号,立即返回不等待
  3. 处理阶段: 异步线程执行耗时操作
  4. 完成阶段: 在主线程调用完成回调(如果提供)
  5. 清理阶段: 在 _process() 中处理完成的异步任务

异步 vs 同步

  • 同步: emit() 会阻塞直到所有订阅者完成处理
  • 异步: emit_async() 立即返回,处理在后台进行

回调机制

  • 异步回调: 在异步线程中执行,用于耗时操作
  • 完成回调: 在主线程中执行,用于处理结果
  • 错误回调: 在主线程中执行,用于处理错误

任务管理

gdscript
# 获取活动异步任务数量
bus.get_active_async_task_count()

# 处理完成的异步任务(必须在 _process 中调用)
bus.process_async_tasks()

最佳实践

1. 总是调用 process_async_tasks()

gdscript
func _process(_delta):
    bus.process_async_tasks()  # 必须在每帧调用

2. 合理使用回调

gdscript
# 对于简单的异步操作,使用基础订阅
bus.subscribe_async("simple_task", async_handler)

# 对于需要结果处理的操作,使用带回调的订阅
bus.subscribe_async_with_callbacks("complex_task", 
    async_handler, 
    complete_callback, 
    error_callback
)

3. 避免长时间阻塞

gdscript
# 将长时间操作分解为小块
func long_running_task(data):
    for i in range(100):
        process_chunk(data[i])
        if i % 10 == 0:  # 每10个块检查一次
            OS.delay_msec(1)  # 让出控制权

4. 错误处理

gdscript
# 总是提供错误回调
var error_callback = func(error):
    print("异步操作失败: ", error)
    # 实施回退策略

bus.subscribe_async_with_callbacks("risky_task",
    async_handler,
    complete_callback,
    error_callback
)

这个异步示例展示了如何在不阻塞主线程的情况下处理耗时操作,是构建响应式游戏应用的重要工具。

基于 MIT 许可发布