0

我像这个例子一样创建了一个双工服务(NetTcpBinding):我使用了发布-订阅模式,并且对于来自新客户端的每个连接请求,它都会创建一个新的服务实例(包含不同的回调)。在此示例中,回调是通过事件和委托调用的。

现在我想改变这个例子:假设我们不想立即响应客户端的请求,即假设我们想在一定的时间间隔后调用回调方法。在这种情况下,我需要维护对回调方法的引用......但是如果同时某些客户端断开连接会发生什么?服务实例被销毁,我们甚至失去了回调......

我写了这个例子:

  • MySingletonTable是存储对回调方法的引用的数据结构;
  • SampleService不是服务,而是模拟服务的实例;

    public delegate void ProcessingHandler(string item, double price, double change);
    
    public class MySingletonTable
    {
        private static volatile MySingletonTable m_Instance;
        private static object syncRoot = new object();
    
        private static Dictionary<string, ProcessingHandler> pointersToHandlers;
    
        private MySingletonTable()
        {
            pointersToHandlers = new Dictionary<string, ProcessingHandler>();
        }
    
        // Return the singleton instance of this class.
        public static MySingletonTable Instance
        {
            get
            {
                if (m_Instance == null)
                {
                    lock (syncRoot)
                    {
                        if (m_Instance == null)
                            m_Instance = new MySingletonTable();
                    }
                }
                return m_Instance;
            }
        }
    
        /// The number of the entries in the table.
        public int Count
        {
            get
            {
                lock (syncRoot)
                {
                    return pointersToHandlers.Count;
                }
            }
        }
    
        // Add an handler.
        public void Add(string id, ProcessingHandler handler)
        {
            lock (syncRoot)
            {
                if (!pointersToHandlers.ContainsKey(id))
                    pointersToHandlers.Add(id, handler);
            }
        }
    
        // Get an handler from the table.
        public ProcessingHandler GetHandler(string id)
        {
            ProcessingHandler handler = null;
            lock (syncRoot)
            {
                if (pointersToHandlers.ContainsKey(id))
                    handler = pointersToHandlers[id];
            }
            return handler;
        }
    
        // Remove the specified handler.
        public bool Remove(string id)
        {
            lock (syncRoot)
            {
                return pointersToHandlers.Remove(id);
            }
        }
    }
    
    // This class simulates the service.
    public class SampleService
    {
        private static int counter = 0;
        private int service_i = ++counter;
    
        MySingletonTable reference = MySingletonTable.Instance;
    
        public SampleService(string id)
        {
            reference.Add(id, PriceChange);
        }
    
        private void PriceChange(string item, double price, double change)
        {
            // call the callback
            // ...
            Console.WriteLine("service_i {0}: {1} {2} {3}", service_i, item, price, change);
        }
    }
    
    
    class Program
    {
        static void Main(string[] args)
        {
            SampleService s1 = new SampleService("abc");
            SampleService s2 = new SampleService("def");
    
            MySingletonTable table = MySingletonTable.Instance;
            ProcessingHandler handler = null;
    
            handler = table.GetHandler("abc");
            handler("item one", 10, 20);
    
            handler = table.GetHandler("def");
            handler("item two", 30, 40);
    
            Console.ReadLine();
        }
    }
    

显然我不能明确地销毁这个例子中模拟的两个服务实例。但是,如果一个服务的两个实例与两个不同的客户端相关,会发生什么s1情况s2

4

3 回答 3

1

如果您的委托存储在 pointersToHandlers 中,那么它将保留您的对象,因此它永远不会被垃圾收集,或者您称之为“销毁”。实际上,您现在也有内存泄漏。

当客户端断开连接时(或者当您期望对象被销毁时),您将需要从列表中删除委托。不知道你为什么不使用事件,但这是另一个问题。即使您确实使用了事件,一旦完成,您仍然需要断开连接,以免最终出现这种情况。

于 2012-06-25T21:08:23.210 回答
1

好的,您正在将局部变量 S1 和 S2 设置为空。但是,字典仍然具有该对象的地址。当您执行 s1 = null 时,new 运算符创建的 SampleService 不会被删除,这就是为什么调用 handler after 仍然有效的原因。这与做同样的事情:

var s1 = new SampleService("aa");
var handler = s1;
s1 = null;

处理程序仍在引用创建的对象。

于 2012-06-25T22:08:17.493 回答
0

怎么样...

if (kDelegate.Target != null) kDelegate();
于 2014-04-11T01:10:06.957 回答