【问题标题】:Allocation free delegate or other way to call method by address?分配免费委托或其他按地址调用方法的方式?
【发布时间】:2014-01-17 04:47:42
【问题描述】:

我需要能够在 C# 中使用 Mono 调用基于函数指针的单个方法。委托为此工作得很好,这是他们的目的,但他们似乎每次我设置委托时分配 52 个字节(不是 +=,而是使用 = 设置它,所以委托引用的方法总是一个且只有一个)。

此委托每秒更改多次,它会导致 GC 定期启动,这是我想避免的。

我不介意初始内存分配,但有没有办法在我每次更改单个委托值时阻止分配?

如果没有,除了每次地址更改时不会分配任何内存的委托之外,还有其他动态方式来调用 C# 中的方法吗?

【问题讨论】:

  • 代理经常变化的事实似乎表明您的设计可能是错误的树。不是答案,不是我如何阻止我的委托使用这么多内存,而是更多,我如何阻止委托如此频繁地更改
  • 您能否详细说明为什么您一直在更换代表?可能还有其他选择。
  • 我确实发现实际问题很有趣。想知道是否有办法阻止代表分配
  • 我有一堆用于游戏的状态机。为了使状态机易于管理和升级,它使用单个委托来分支状态,而不是巨大的 switch/case 语句。还允许将状态机分成多个文件以便于维护。世界上数百个参与者以每秒 60 帧的速度运行,并且都使用状态机导致数百个 52 字节的小分配,然后每秒左右 GC 启动并导致打嗝。
  • @ServéLaurijssen blogs.msdn.com/b/shawnhar/archive/2007/07/09/… It is actually impossible to construct a new delegate without allocating memory

标签: c# delegates mono function-pointers


【解决方案1】:

你这样写的任何代码

Action action = foo.DoSomething;

最终被编译成这个

Action action = new Action(foo.DoSomething);

分配来自哪里。没有任何完美的方法可以解决这个问题,但要防止您需要缓存和重用委托的分配。

实现端修复

您可以通过为每个方法创建一个委托来在实现方面实现这一点。

public class Foo
{
    public void DoSomething() { /*nop*/ }

    private Action _doSomethingDelegate;
    public Action DoSomethingDelegate
    {
        get { return _doSomethingDelegate ?? (_doSomethingDelegate = DoSomething); }
    }
}

那么你将只引用现有的委托而不是方法

Action action = foo.DoSomethingDelegate;

缓存修复

另一种选择是使用某种缓存类,但这会引入一大堆对象生命周期问题,您可能不希望在游戏场景中出现这些问题。这是一个有点粗略的实现,真正的人可能想要使用弱引用。

public static class DelegateCache
{
    private static readonly Dictionary<object, Dictionary<string, Delegate>> Cache = new Dictionary<object, Dictionary<string, Delegate>>();

    private static Dictionary<string, Delegate> GetObjectCache(object instance)
    {
        Dictionary<string, Delegate> delegates;
        if (!Cache.TryGetValue(instance, out delegates))
        {
            Cache[instance] = delegates = new Dictionary<string, Delegate>();
        }
        return delegates;
    }

    public static T GetDelegate<T>(object instance, string method)
        where T: class
    {
        var delegates = GetObjectCache(instance);
        Delegate del;
        if (!delegates.TryGetValue(method, out del))
        {
            delegates[method] = del = Delegate.CreateDelegate(typeof(T), instance, method);
        }
        return del as T;
    }
}

使用它看起来像这样

Action action = DelegateCache.GetDelegate<Action>(foo, "DoSomething");

总结

运行一些测试,这两种方法对于每个对象/方法对只有一个分配。我可能会去实施方面修复它,尽管它需要做很多工作,但它会更干净。如果有很多方法,并且您计划添加更多方法,则可以使用 T4 生成一个部分类,其中包含方法的委托实现。

【讨论】:

    猜你喜欢
    • 2015-04-22
    • 2014-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-25
    • 2011-09-15
    • 2011-07-23
    相关资源
    最近更新 更多