使用弱引用和GC进行渲染 [英] Rendering using Weak References, and the GC

查看:143
本文介绍了使用弱引用和GC进行渲染的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题



我最近开始学习 C#。我是通过制作游戏来做到这一点的(因为我很熟悉 C ++ )。



对象将被绘制到后台缓冲区,在施工时通过对该对象的弱引用发送的事件进行注册。使用 C ++ 智能指针作为引用计数,并且(因此)只要最后一个引用超出范围就会销毁对象,这对我的目的非常有用。然而,在 C#中,这当然不是这种情况。



考虑下面的代码片段:

  foreach(WeakReference< DrawableObject> drawable in drawables.ToList())
{
DrawableObject目标;
drawable.TryGetTarget(out target);
if(target!= null)
{
spriteBatch.Draw(target.Texture,target.Position,target.Rectangle,target.Colour);
}
else
{
drawables.Remove(drawable);


$ / code $ / pre

主要问题(因为我确定有最有可能的其他人我还没有理解)在 C#中, target 不能保证是<$
$ b




null 在对象的最后一个强引用超出了作用域之后。

h2>潜在解决方案

强制垃圾回收

我可以强制调用GC在适当的时间提前,但经过对这个想法的一些研究后,我发现这很可能是一个坏主意和/或代码中其他问题的迹象。



<使用Hide()方法



虽然这种方法对于任何游戏都很重要,但必须显式调用 Hide()每一个可绘制的对象都很乏味,给出了更多的错误空间。此外,当应该调用所有这些 Hide()吗?我不能依靠收集的父对象,因此在 Finalizer 中播放它们不会解决问题。



实现IDisposable



与上面类似,使用 >声明(我不知道在哪里放置,因为对象需要活着直到父母超出范围)。

$ b

问题



我所能想到的解决方案似乎都不能解决问题。我当然错过了不以这种方式使用弱引用的想法,但是如果没有找到可以在当前状态下与游戏一起工作的适当解决方案,则必须考虑这样做。所有这些都归结为一些相关的问题:


  • 这是强制垃圾收集的合适用例 strong>?


  • 如果不是,我如何确定一个对象是否是到期当GC的下一次运行发生时?


  • 有没有办法在最后一次调用对象的方法
    的引用超出了范围(隐式或显式地),
    而不是等待GC出现?


  • ul>

    我当然非常感谢任何可以避免这个问题的建议。

    解决方案

    我会在此尝试一个答案。



    你在找什么是我死了根据规则的游戏的逻辑。不是那个对象已经死了,因为垃圾收集器这么说逻辑。

    为了达到这个效果,你需要把你的想法转换成这样的东西:

      class DrawableObject {
    public bool IsDead {get;组; }
    public int Health {get;组; }

    // ...
    public void GetShot(int amount){
    Health - = amount;

    if(Health <= 0)
    IsDead = true;


    ..然后:

      foreach(drawable中的var drawable){

    DrawableObject target;
    drawable.TryGetTarget(out target);

    if(target == null || target.IsDead){
    drawables.Remove(drawable);
    }
    else {
    spriteBatch.Draw(target.Texture,target.Position,target.Rectangle,target.Colour);






    如果它的 null
    code>基于你的 TryGetXXX 逻辑......或者它已经被你的游戏逻辑标记为死亡了......将它从可绘制对象列表中移除。让垃圾收集器随时清理它。



    TLDR:将您的对象标记为已死,并将您的逻辑基于您自己的我已死标志。 / p>

    The Problem

    I've recently started learning C#. I am doing this through making a game (as I am quite familiar with this in C++).

    Objects that are to be drawn to the back buffer are 'registered' upon construction via an event being sent with a weak reference to the object. With C++ smart pointers being reference counted and (as a result) objects being destroyed as soon as the last reference goes out of scope, this works well for my purposes. However, in C#, this is certainly not the case.

    Consider the following code snippet:

    foreach(WeakReference<DrawableObject> drawable in drawables.ToList())
    {
        DrawableObject target;
        drawable.TryGetTarget(out target);
        if(target != null)
        {
            spriteBatch.Draw(target.Texture, target.Position, target.Rectangle, target.Colour);
        }
        else
        {
            drawables.Remove(drawable);
        }
    }
    

    The main problem (as I'm sure there are most likely others I am yet to understand) with this in C#, is that target is not guaranteed to be null after the last strong reference to the object goes out of scope.


    Potential Solutions

    Forcing garbage collection

    I could force invocation of the GC at an appropriate time beforehand, but after some research into the idea I have discovered that this is most likely a bad idea and/or a sign of other issues in the code.

    Using a Hide() method

    Whilst such a method is essential to any game, having to explicitly call Hide() every single drawable object is tedious and gives more room for error. Furthermore, when should all these Hide()'s be called? I cannot rely on the parent object being collected in time either, so playing them in a Finalizer would not solve the problem.

    Implementing IDisposable

    Similar to the above, with the alternative of a using statement (which I would not know where to place, since the objects need to be alive until their parents go out of scope).


    The Question(s)

    None of the solutions I could think of seem to appropriately tackle the problem. I of course missed out the idea of not using weak references in this way whatsoever, but will have to consider doing so if no proper solution is found that can work with the game in its current state. All this boils down to a few related questions:

    • Is this an appropriate use case for forcing garbage collection?

    • If not, how can I determine whether an object is due to be collected when the next run of the GC occurs?

    • Is there a way to call a method of an object as soon as the last reference to it goes out of scope (either implicitly or explicitly), rather than waiting for the GC to come along?

    I am, of course, grateful for any suggestions that would avoid this problem altogether too.

    解决方案

    I'm going to attempt an answer at this..

    What you're looking for is "I am dead according to the rules of the game" logic. Not "That object is dead because the Garbage Collector says so" logic.

    To that effect.. you need to flip your thinking to something like this:

    class DrawableObject {
        public bool IsDead { get; set; }
        public int Health { get; set; }
    
        // ...
        public void GetShot(int amount) {
            Health -= amount;
    
            if (Health <= 0)
                IsDead = true;
        }
    }
    

    ..then:

    foreach (var drawable in drawables) {
    
        DrawableObject target;
        drawable.TryGetTarget(out target);
    
        if(target == null || target.IsDead) {
            drawables.Remove(drawable);
        }
        else {
            spriteBatch.Draw(target.Texture, target.Position, target.Rectangle, target.Colour);
        }
    }
    

    If its null based on your TryGetXXX logic.. or it has been marked as dead by your game logic.. remove it from the list of drawable objects. Let the garbage collector clean it up whenever it wants after that.

    TLDR: Mark your objects as dead and base your logic around your own "I am dead" flag.

    这篇关于使用弱引用和GC进行渲染的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆