有没有办法使用GDScript词典来防止这种种族条件?

发布于 2025-02-12 09:19:33 字数 815 浏览 1 评论 0原文

我正在使用GDScript中的以下函数:

func remove_entity(ent: Sprite):
    sprite_lst[ent].queue_free()
    sprite_lst.erase(ent)
    draw_entities()

sprite_lst是一本词典,它包含HUD上的所有精灵。这些精灵的键是它们代表的实际对象。该功能被称为从HUD中删除其中一种。

然而,这大多有效,似乎有种族条件,阻止了比赛总是按预期工作的。有时,此函数在尝试调用queue_free()时会引发异常,因为entsprite_lst ent。 >。

要进行故障排除,我尝试在擦除对象ID之前尝试打印出对象ID。但是,一旦我向此功能添加了打印语句,它就停止了异常。因此,我将功能更改为:

func remove_entity(ent: Sprite):
    var this_sprite = sprite_lst[ent]
    this_sprite.queue_free()
    sprite_lst.erase(ent)
    draw_entities()

当从sprite_lst中存储实体时,该函数始终有效。在我看来,在queue_free()之前,在词典上调用erase()方法,在Sprite上调用。

有什么方法可以防止这种比赛状况,或者迫使这两条线依次运行?

I am working with the following function in GDScript:

func remove_entity(ent: Sprite):
    sprite_lst[ent].queue_free()
    sprite_lst.erase(ent)
    draw_entities()

sprite_lst is a Dictionary that holds all of the Sprites on the HUD. The keys for these Sprites are the actual object that they're representing. This function is called to remove one of those sprites from the HUD.

This mostly works alright, however, it seems to have a race condition preventing it from always working as intended. Sometimes, this function will throw an exception when trying to call queue_free() on the intended object, because ent is not found as a key in sprite_lst.

To troubleshoot, I tried printing out the object ID before erasing it. However, once I added a print statement to this function, it stopped throwing the exception. So then I changed the function to this:

func remove_entity(ent: Sprite):
    var this_sprite = sprite_lst[ent]
    this_sprite.queue_free()
    sprite_lst.erase(ent)
    draw_entities()

When storing the entity from sprite_lst, the function always works. It seems to me like sometimes the erase() method is called on the dictionary, before queue_free() is called on the Sprite.

Is there any way to prevent this race condition, or force these two lines to run sequentially?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

轻拂→两袖风尘 2025-02-19 09:19:33

在我看来,有时erase()方法在字典上调用,在queue_free()在Sprite上调用。

在代码中,您始终首先调用queue_free()。但是,这排队免费。它类似于call_deferred(“ free”)(并不完全相同,但这可以使这个想法跨越)。


我们可以在您的代码中应用大量的防御编程。

首先,我们可以检查该条目是否存在:

func remove_entity(ent: Sprite):
    if sprite_lst.has(ent):
        # …
        pass

或者我们可以使用get,如果不是这样,则可以返回null:

func remove_entity(ent: Sprite):
    var this_sprite = sprite_lst.get(ent)
    # …

第二,我们可以检查是否有一个有效的实例(不是null,而不是释放):

func remove_entity(ent: Sprite):
    var this_sprite = sprite_lst.get(ent)
    if is_instance_valid(this_sprite):
        this_sprite.queue_free()

    # …

这样,queue_free呼叫上不应有例外。

我想您也想删除任何无效的条目,对吗?值得庆幸的是,擦除不会抱怨不在那里的钥匙,因此我们不需要支票来保护它。

func remove_entity(ent: Sprite):
    var this_sprite = sprite_lst.get(ent)
    if is_instance_valid(this_sprite):
        this_sprite.queue_free()

    sprite_lst.erase(ent)
    draw_entities()

好吧,这会照顾错误。但是,您为什么要关心节点何时获释?我会冒险猜测您关心的原因是因为您依靠它从场景树中删除节点。我们可以直接解决这个问题:

func remove_entity(ent: Sprite):
    var this_sprite:Node = sprite_lst.get(ent)
    if is_instance_valid(this_sprite):
        if this_sprite.is_inside_tree():
            this_sprite.get_parent().remove_child(this_sprite)

        this_sprite.queue_free()

    sprite_lst.erase(ent)
    draw_entities()

我想您可以做到这一点(这是不一样的,因为这也在场景树之外起作用,但是我认为我们不必担心这种情况):

func remove_entity(ent: Sprite):
    var this_sprite:Node = sprite_lst.get(ent)
    if is_instance_valid(this_sprite):
        var parent:Node = this_sprite.get_parent()
        if parent != null:
            parent.remove_child(this_sprite)

        this_sprite.queue_free()

    sprite_lst.erase(ent)
    draw_entities()

在这种情况下,请注意请注意,尝试从另一个不是父母的节点中删除node,这不是父母会导致错误。因此,即使您知道应该是父母的,您仍然需要一张检查:

func remove_entity(ent: Sprite):
    var this_sprite:Node = sprite_lst.get(ent)
    if is_instance_valid(this_sprite):
        if parent == this_sprite.get_parent():
            parent.remove_child(this_sprite)

        this_sprite.queue_free()

    sprite_lst.erase(ent)
    draw_entities()

或者,如果它对您有用,则可以避免使用remove_and_skip的父级shenanigans,例如:

func remove_entity(ent: Sprite):
    var this_sprite:Node = sprite_lst.get(ent)
    if is_instance_valid(this_sprite):
        if this_sprite.is_inside_tree():
            this_sprite.remove_and_skip()

        this_sprite.queue_free()

    sprite_lst.erase(ent)
    draw_entities()

请注意,emove_and_and_skip < /代码>将节点的孩子添加回场景树。 remove_child不会(节点将使孩子在场景树外面)。


为了完整,我还会提到:

  • 擦除返回是否删除了某些内容。
  • 您可以使用IS_QUEUD_FOR_DELETIONObject> Object上检查queue_free

还提醒您,释放node并不能使其所有引用null相反,它们变得无效,这就是为什么is_instance_valid并检查如果不是null不是同一回事。

It seems to me like sometimes the erase() method is called on the dictionary, before queue_free() is called on the Sprite.

In your code, you always call queue_free() first. However, that queues a free. It is similar to call_deferred("free") (not exactly the same, but this gets the idea across).


We can apply plenty of defensive programming to your code.

First of all, we can check if the entry is there:

func remove_entity(ent: Sprite):
    if sprite_lst.has(ent):
        # …
        pass

Or we can use get which will return null if it isn't:

func remove_entity(ent: Sprite):
    var this_sprite = sprite_lst.get(ent)
    # …

Second we can check if we got a valid instance (not null and not freed):

func remove_entity(ent: Sprite):
    var this_sprite = sprite_lst.get(ent)
    if is_instance_valid(this_sprite):
        this_sprite.queue_free()

    # …

That way there should be no exception on the queue_free call.

I suppose you want to remove any invalid entry, too, right? Thankfully erase won't complain about a key not being there, so we don't need a check to guard it.

func remove_entity(ent: Sprite):
    var this_sprite = sprite_lst.get(ent)
    if is_instance_valid(this_sprite):
        this_sprite.queue_free()

    sprite_lst.erase(ent)
    draw_entities()

Alright, that takes care of the error. However, why do you care about when the node gets freed? I'll venture to guess that the reason you care is because you rely on it to remove the node from the scene tree. We could address that directly:

func remove_entity(ent: Sprite):
    var this_sprite:Node = sprite_lst.get(ent)
    if is_instance_valid(this_sprite):
        if this_sprite.is_inside_tree():
            this_sprite.get_parent().remove_child(this_sprite)

        this_sprite.queue_free()

    sprite_lst.erase(ent)
    draw_entities()

I suppose you could do this instead (which isn't the same, because this also works outside of the scene tree, but I don't think we need to worry about that case):

func remove_entity(ent: Sprite):
    var this_sprite:Node = sprite_lst.get(ent)
    if is_instance_valid(this_sprite):
        var parent:Node = this_sprite.get_parent()
        if parent != null:
            parent.remove_child(this_sprite)

        this_sprite.queue_free()

    sprite_lst.erase(ent)
    draw_entities()

On that note, be aware that attempting to remove a Node from another Node which isn't the parent will cause an error. So even if you know which should be parent, you still need a check:

func remove_entity(ent: Sprite):
    var this_sprite:Node = sprite_lst.get(ent)
    if is_instance_valid(this_sprite):
        if parent == this_sprite.get_parent():
            parent.remove_child(this_sprite)

        this_sprite.queue_free()

    sprite_lst.erase(ent)
    draw_entities()

Or, if it works for you, you could avoid parent shenanigans with remove_and_skip, for example:

func remove_entity(ent: Sprite):
    var this_sprite:Node = sprite_lst.get(ent)
    if is_instance_valid(this_sprite):
        if this_sprite.is_inside_tree():
            this_sprite.remove_and_skip()

        this_sprite.queue_free()

    sprite_lst.erase(ent)
    draw_entities()

Be aware that remove_and_skip will add the children of the node back to the scene tree. While remove_child would not (the Node will keep children outside the scene tree).


For completeness I'll also mention that:

  • erase returns whether or not it removed something.
  • You can use is_queued_for_deletion to check if you called queue_free on an Object.

And also remind you that freeing a Node does not make all the reference to it null, instead they become invalid, which is why is_instance_valid and checking if it isn't null aren't the same thing.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文