Skip to content

异步信号

概述

异步信号允许在后台线程中处理耗时操作,避免阻塞主线程,保持游戏的流畅性。GDSignalBus 提供了完整的异步信号处理机制。

基础用法

发送异步信号

gdscript
# 获取 SignalBus 单例
var bus = SignalBus.get_singleton()

# 基本异步信号
bus.emit_async("save_game", [player_data])

# 带回调的异步信号
bus.subscribe_async_with_callbacks("data_processing",
    func(args):
        var data = args[0]
        print("[异步线程] 开始处理数据: ", data)
        OS.delay_msec(2000)  # 模拟耗时操作
        return "处理结果: " + str(data * 2),
    func(result):
        print("[主线程] 处理完成,结果: ", result),
    func(error):
        print("[主线程] 处理失败: ", error)
)

# 发射异步信号
bus.emit_async("data_processing", [42])

异步回调

gdscript
func _on_save_complete(success, result):
    if success:
        print("保存成功: ", result)
    else:
        print("保存失败: ", result)

func _on_load_complete(success, data):
    if success:
        apply_loaded_data(data)
    else:
        show_error_message("加载失败")

异步任务类型

文件操作

gdscript
# 异步文件读取
bus.subscribe_async_with_callbacks("file_read",
    func(args):
        var path = args[0]
        var file = FileAccess.open(path, FileAccess.READ)
        if file:
            var content = file.get_as_text()
            file.close()
            return content
        return null,
    _on_file_read_complete,
    _on_file_read_error
)

# 异步文件写入
bus.subscribe_async_with_callbacks("file_write",
    func(args):
        var path = args[0]
        var content = args[1]
        var file = FileAccess.open(path, FileAccess.WRITE)
        if file:
            file.store_string(content)
            file.close()
            return true
        return false,
    _on_file_write_complete,
    _on_file_write_error
)

网络请求

gdscript
# HTTP 请求
bus.subscribe_async_with_callbacks("http_request",
    func(args):
        var url = args[0]
        var method = args[1]
        var body = args[2] if args.size() > 2 else ""
        
        var http = HTTPRequest.new()
        # 这里需要实现实际的 HTTP 请求逻辑
        # 简化示例,返回模拟结果
        OS.delay_msec(1000)  # 模拟网络延迟
        return "Response from " + url,
    _on_response_complete,
    _on_response_error
)

数据处理

gdscript
# 复杂计算
bus.subscribe_async_with_callbacks("calculate_path",
    func(args):
        var start = args[0]
        var end = args[1]
        
        print("[异步线程] 开始计算路径...")
        
        # 模拟 A* 寻路算法
        var path = []
        var current = start
        
        while current.distance_to(end) > 1.0:
            var next = current + (end - current).normalized() * 10.0
            path.append(next)
            current = next
            OS.delay_msec(50)  # 模拟计算时间
        
        path.append(end)
        print("[异步线程] 路径计算完成")
        return path,
    _on_path_calculated,
    _on_path_error
)

# 大量数据处理
bus.subscribe_async_with_callbacks("process_inventory",
    func(args):
        var items = args[0]
        print("[异步线程] 开始处理库存数据...")
        
        var processed_items = []
        for item in items:
            # 模拟复杂计算
            OS.delay_msec(100)
            
            var processed_item = {
                "id": item.id,
                "name": item.name,
                "value": item.value * 1.5
            }
            processed_items.append(processed_item)
        
        print("[异步线程] 库存处理完成")
        return processed_items,
    _on_inventory_processed,
    _on_inventory_error
)

高级功能

任务管理

gdscript
# 获取活动异步任务数量
var active_tasks = bus.get_active_async_task_count()
print("活动异步任务: ", active_tasks)

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

# 等待所有异步任务完成
bus.wait_all_async_tasks()

# 取消特定异步任务
var task_id = bus.emit_async("long_operation", [data])
bus.cancel_async_task(task_id)

任务状态监控

gdscript
func _ready():
    # 设置定时器监控异步任务状态
    var timer = Timer.new()
    timer.wait_time = 1.0
    timer.timeout.connect(_monitor_async_tasks)
    timer.autostart = true
    add_child(timer)

func _monitor_async_tasks():
    var active_count = bus.get_active_async_task_count()
    if active_count > 5:
        print("警告: 活动异步任务过多: ", active_count)

错误处理

超时处理

gdscript
# 使用带错误回调的异步订阅
bus.subscribe_async_with_callbacks("network_request",
    func(args):
        var url = args[0]
        # 模拟可能失败的操作
        OS.delay_msec(2000)
        if randf() < 0.3:  # 30% 失败率
            return null  # 返回 null 表示失败
        return "Success response",
    func(result):
        print("请求成功: ", result),
    func(error):
        print("请求失败: ", error)
)

重试机制

gdscript
# 实现重试逻辑
func retry_async_operation(signal_name, args, max_retries=3):
    var retry_count = 0
    
    func attempt_operation():
        bus.subscribe_async_with_callbacks(signal_name + "_retry",
            func(op_args):
                # 执行实际操作
                return perform_operation(op_args[0]),
            func(result):
                print("操作成功")
                cleanup_retry_subscription(signal_name + "_retry"),
            func(error):
                retry_count += 1
                if retry_count < max_retries:
                    print("重试 ", retry_count, "/", max_retries)
                    attempt_operation()
                else:
                    print("重试次数用尽,操作失败")
                    cleanup_retry_subscription(signal_name + "_retry")
        )
        
        bus.emit(signal_name + "_retry", [args])
    
    attempt_operation()

性能优化

批量处理

gdscript
# 将多个小任务合并为一个批量任务
var items_to_process = []

func add_item_to_queue(item):
    items_to_process.append(item)
    
    # 达到批量大小或定时处理
    if items_to_process.size() >= 10:
        process_batch()

func process_batch():
    if items_to_process.size() > 0:
        bus.emit_async("process_inventory_batch", [items_to_process.duplicate()])
        items_to_process.clear()

资源管理

gdscript
func _exit_tree():
    # 等待所有异步任务完成
    bus.wait_all_async_tasks()
    
    # 或者取消特定任务
    # bus.cancel_async_task(task_id)

最佳实践

1. 合理使用异步

gdscript
# 适合异步的操作
- 文件 I/O
- 网络请求
- 复杂计算
- 大量数据处理

# 不适合异步的操作
- 简单状态更新
- UI 相关操作
- 需要立即响应的操作

2. 错误处理

gdscript
func _on_async_complete(success, result):
    if success:
        handle_success(result)
    else:
        handle_error(result)
        # 考虑重试或回退方案

3. 资源管理

gdscript
func _exit_tree():
    # 取消该对象的所有异步任务
    bus.unsubscribe_all(self)

调试和监控

异步任务监控

gdscript
func _ready():
    # 启用调试模式
    bus.set_debug_enabled(true)

func _process(_delta):
    # 处理异步任务
    bus.process_async_tasks()
    
    # 定期检查任务状态
    if Engine.get_frames_drawn() % 60 == 0:  # 每秒检查一次
        var active_tasks = bus.get_active_async_task_count()
        if active_tasks > 10:
            print("警告: 活动异步任务过多: ", active_tasks)

示例:完整的异步保存系统

gdscript
# SaveManager.gd
extends Node

var bus

func _ready():
    bus = SignalBus.get_singleton()
    
    # 订阅异步保存信号
    bus.subscribe_async_with_callbacks("save_game",
        func(args):
            var save_data = args[0]
            var path = args[1]
            
            print("[异步线程] 开始保存游戏...")
            OS.delay_msec(2000)  # 模拟保存时间
            
            var file = FileAccess.open(path, FileAccess.WRITE)
            if file:
                file.store_var(save_data)
                file.close()
                return true
            return false,
        _on_save_complete,
        _on_save_error
    )

func save_game_async():
    var save_data = prepare_save_data()
    var save_path = "user://savegame.dat"
    
    # 显示保存提示
    show_saving_indicator()
    
    # 异步保存
    bus.emit_async("save_game", [save_data, save_path])

func _on_save_complete(success):
    hide_saving_indicator()
    
    if success:
        show_message("游戏保存成功!")
        update_save_timestamp()
    else:
        show_error("保存失败")

func _on_save_error(error):
    hide_saving_indicator()
    show_error("保存出错: " + str(error))

func _process(_delta):
    # 必须在主线程处理异步任务
    bus.process_async_tasks()

func _exit_tree():
    # 取消所有未完成的保存任务
    bus.unsubscribe_all(self)

通过异步信号系统,你可以构建响应迅速、用户体验良好的游戏应用,同时保持代码的简洁和可维护性。

基于 MIT 许可发布