Work in progress
The content of this page was not yet updated for Godot
4.5
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
Потоки
Потоки используются для балансировки вычислительной мощности между процессорами и ядрами. Godot поддерживает многопоточность, но не во всем движке.
Ниже приведен список способов, которыми многопоточность может быть использована в различных областях Godot.
Глобальная область
Все синглтоны Global Scope потокобезопасны. Поддерживается доступ к серверам из потоков (для серверов RenderingServer и Physics убедитесь, что в настройках проекта включена потоковая или потокобезопасная работа!).
Это делает их отличными для кода который создаёт многие тысячи экземпляров в серверах и контролирует их из потоков. Конечно, это требует немного больше кода, так как они используются напрямую, а не через дерево сцены.
Дерево сцены
Взаимодействие с деревом активной сцены НЕ потоко-безопасно. Обязательно используйте мьютексы при передаче данных между потоками. Если вы хотите вызывать функции из потока, можно использовать функцию 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 загружаются только один раз) будут изменены несколькими потоками, что приведёт к неожиданному поведению или сбоям.
Используйте более одного потока для генерации данных сцены только в том случае, если вы действительно понимаете, что делаете, и уверены, что один и тот же ресурс не используется и не задаётся несколькими. В противном случае безопаснее напрямую использовать API сервера (полностью потоко-безопасный), не трогая сцену или ресурсы.
Отрисовка
Создание экземпляров узлов, которые рендерят что-либо в 2D или 3D (например, спрайт), по умолчанию не потоко-безопасно. Чтобы сделать рендеринг потоко-безопасным, установите для параметра проекта Rendering > Driver > Thread Model значение Multi-Threaded.
Обратите внимание, что Multi-Threaded (много-поточная) модель потоков имеет несколько известных ошибок, поэтому ее нельзя использовать во всех сценариях.
Следует избегать вызова функций, предполагающих прямое взаимодействие с графическим процессором в других потоках, таких как создание новых текстур или изменение и извлечение данных изображения. Эти операции могут привести к снижению производительности, поскольку требуют синхронизации с RenderingServer, поскольку данные необходимо передавать на графический процессор или обновлять на нем.
GDScript массивы, словари
В GDScript чтение и запись элементов из нескольких потоков допустимы, но все, что изменяет размер контейнера (изменение размера, добавление или удаление элементов), требует блокировки мьютекса.
Ресурсы
Изменение уникального ресурса из нескольких потоков не поддерживается. Однако работа со ссылками в нескольких потоках поддерживается, поэтому загрузка ресурсов в поток также поддерживается - сцены, текстуры, сетки и т.д. могут быть загружены и обработаны в потоке, а затем добавлены в активную сцену в основном потоке. Ограничением здесь является то, что, как описано выше, нужно быть осторожным, чтобы не загрузить один и тот же ресурс из нескольких потоков одновременно, поэтому проще всего использовать один поток для загрузки и изменения ресурсов, а затем основной поток для их добавления.