Work in progress
The content of this page was not yet updated for Godot
4.4
and may be outdated. If you know how to improve this page or you can confirm
that it's up to date, feel free to open a pull request.
线程安全的 API
线程
线程是用来平衡各CPU和核心的处理能力.Godot支持多线程, 但不是在整个引擎中.
下面是可以在Godot的不同区域使用多线程的方法列表.
全局作用域
全局范围单例都是线程安全的。支持从线程访问服务器(对于 RenderingServer 和 Physics 服务器,请确保在项目设置中启用了线程或线程安全操作!)。
这使它们成为在服务器中创建数十万个实例并从线程控制它们的代码的理想选择. 当然, 还需要更多的代码, 因为这是直接使用的而不是嵌入场景树中使用.
场景树
与活动场景树的交互是线程 不 安全的. 当在线程之间发送数据时, 请确保使用mutexes. 如果你想从一个线程调用函数, 可以使用 call_deferred 函数:
# Unsafe:
node.add_child(child_node)
# Safe:
node.add_child.call_deferred(child_node)
// Unsafe:
node.AddChild(childNode);
// Safe:
node.CallDeferred(Node.MethodName.AddChild, childNode);
但是, 可以在激活的场景树外创建场景块(以树形式排列的节点). 这样, 可以在线程中构建或实例化部分场景, 然后将其添加到主线程中:
var enemy_scene = load("res://enemy_scene.scn")
var enemy = enemy_scene.instantiate()
enemy.add_child(weapon) # Set a weapon.
world.add_child.call_deferred(enemy)
PackedScene enemyScene = GD.Load<PackedScene>("res://EnemyScene.scn");
Node enemy = enemyScene.Instantiate<Node>();
enemy.AddChild(weapon);
world.CallDeferred(Node.MethodName.AddChild, enemy);
不过, 只有当你有 一个 线程加载数据时, 这才真正有用. 从多个线程加载或创建场景块可能有效, 但你要冒着资源被多线程调整的风险(在Godot中只加载一次), 从而导致意外行为或崩溃.
Only use more than one thread to generate scene data if you really know what you are doing and you are sure that a single resource is not being used or set in multiple ones. Otherwise, you are safer just using the servers API (which is fully thread-safe) directly and not touching scene or resources.
渲染
Instancing nodes that render anything in 2D or 3D (such as Sprite) is not thread-safe by default. To make rendering thread-safe, set the Rendering > Driver > Thread Model project setting to Multi-Threaded.
请注意,Multi-Thtreaded 线程模型有若干已知的问题,所以无法胜任所有场景。
你应该避免调用涉及与其他线程上的 GPU 直接交互的函数,例如创建新纹理或修改和检索图像数据,这些操作可能会导致性能停滞,因为它们需要与 RenderingServer 同步,因为数据需要传输到 GPU 或在 GPU 上更新。
GDScript 数组、字典
在 GDScript 中,可以从多个线程读取和写入元素,但是任何改变容器大小的操作(调整大小、添加或删除元素)都需要锁定互斥锁。
资源
不支持从多个线程修改唯一资源。但是支持在多个线程上处理引用,因此在单个线程上加载资源(场景、纹理、网格等)也可以在单个线程上加载和操作,然后添加到主线程上的活动场景中。此处的限制如上所述,必须注意不要同时从多个线程加载相同的资源,因此最简单的方法是使用一个线程来加载和修改资源,然后使用主线程来添加它们。