Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/google-app-engine/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 从多个线程跟踪对对象的WeakReference_Multithreading_C# 4.0_Delegates_Garbage Collection_Weak References - Fatal编程技术网

Multithreading 从多个线程跟踪对对象的WeakReference

Multithreading 从多个线程跟踪对对象的WeakReference,multithreading,c#-4.0,delegates,garbage-collection,weak-references,Multithreading,C# 4.0,Delegates,Garbage Collection,Weak References,我正在设计一个静态消息总线,允许订阅和发布任意类型的消息。为了避免要求观察员明确取消订阅,我希望跟踪指向代理的WeakReference对象,而不是跟踪代理本身。我最终编写了一些类似于保罗·斯托维尔在他的博客中描述的东西 我的问题是:与Paul的代码相反,我的观察者在一个线程上订阅消息,但消息可能在另一个线程上发布。在本例中,我观察到,当我需要通知观察者时,我的WeakReference.Target值为null,表示目标已被收集,即使我确定它们不是。对于短弱引用和长弱引用,问题仍然存在 相反,

我正在设计一个静态消息总线,允许订阅和发布任意类型的消息。为了避免要求观察员明确取消订阅,我希望跟踪指向代理的WeakReference对象,而不是跟踪代理本身。我最终编写了一些类似于保罗·斯托维尔在他的博客中描述的东西

我的问题是:与Paul的代码相反,我的观察者在一个线程上订阅消息,但消息可能在另一个线程上发布。在本例中,我观察到,当我需要通知观察者时,我的WeakReference.Target值为null,表示目标已被收集,即使我确定它们不是。对于短弱引用和长弱引用,问题仍然存在

相反,当订阅和发布从同一线程完成时,代码工作正常。后者是正确的,即使我最终从ThreadPool枚举了一个新线程上的目标,只要请求最初来自我订阅消息的同一个线程

我知道这是一个非常具体的案例,因此非常感谢您的帮助


我的问题是:如果线程同步正确,我是否应该不能从多个线程可靠地访问WeakReference对象?看来我不能,这对我来说没有多大意义。那么,我做得不对的是什么呢?

看起来,在将代码简化为一种更简单的形式(见下文)之后,它现在可以正常工作了。这意味着,导致过早收集弱引用目标的问题必须存在于代码的其他地方。因此,为了回答我自己的问题,弱引用似乎可以从多个线程安全地访问

以下是我的测试代码:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;


namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Starting the app");
            Test test = new Test();
            // uncomment these lines to cause automatic unsubscription from Message1
//          test = null;
//          GC.Collect();
//          GC.WaitForPendingFinalizers();

            // publish Message1 on this thread
            // MessageBus.Publish<Message1>(new Message1());

            // publish Message1 on another thread
            ThreadPool.QueueUserWorkItem(delegate
            {
                MessageBus.Publish<Message1>(new Message1());
            });


            while (!MessageBus.IamDone)
            {
                Thread.Sleep(100);
            }
            Console.WriteLine("Exiting the app");
            Console.WriteLine("Press <ENTER> to terminate program.");
            Console.WriteLine();
            Console.ReadLine();
        }
    }

    public class Test
    {
        public Test()
        {
            Console.WriteLine("Subscribing to message 1.");
            MessageBus.Subscribe<Message1>(OnMessage1);
            Console.WriteLine("Subscribing to message 2.");
            MessageBus.Subscribe<Message2>(OnMessage2);
        }

        public void OnMessage1(Message1 message)
        {
            Console.WriteLine("Got message 1. Publishing message 2");
            MessageBus.Publish<Message2>(new Message2());
        }

        public void OnMessage2(Message2 message)
        {
            Console.WriteLine("Got message 2. Closing the app");
            MessageBus.IamDone = true;
        }
    }

    public abstract class MessageBase
    {
        public string Message;
    }

    public class Message1 : MessageBase
    {
    }

    public class Message2 : MessageBase
    {
    }

    public static class MessageBus
    {
        // This is here purely for this test
        public static bool IamDone = false;
        /////////////////////////////////////

        /// <summary>
        /// A dictionary of lists of handlers of messages by message type
        /// </summary>
        private static ConcurrentDictionary<string, List<WeakReference>> handlersDict = new ConcurrentDictionary<string, List<WeakReference>>();

        /// <summary>
        /// Thread synchronization object to use with Publish calls
        /// </summary>
        private static object _lockPublishing = new object();

        /// <summary>
        /// Thread synchronization object to use with Subscribe calls
        /// </summary>
        private static object _lockSubscribing = new object();

        /// <summary>
        /// Creates a work queue item that encapsulates the provided parameterized message
        /// and dispatches it.
        /// </summary>
        /// <typeparam name="TMessage">Message argument type</typeparam>
        /// <param name="message">Message argument</param>
        public static void Publish<TMessage>(TMessage message)
            where TMessage : MessageBase
        {
            // create the dictionary key
            string key = String.Empty;
            key = typeof(TMessage).ToString();
            // initialize a queue work item argument as a tuple of the dictionary type key and the message argument
            Tuple<string, TMessage, Exception> argument = new Tuple<string, TMessage, Exception>(key, message, null);
            // push the message on the worker queue
            ThreadPool.QueueUserWorkItem(new WaitCallback(_PublishMessage<TMessage>), argument);
        }

        /// <summary>
        /// Publishes a message to the bus, causing observers to be invoked if appropriate.
        /// </summary>
        /// <typeparam name="TArg">Message argument type</typeparam>
        /// <param name="stateInfo">Queue work item argument</param>
        private static void _PublishMessage<TArg>(Object stateInfo)
            where TArg : class
        {
            try
            {
                // translate the queue work item argument to extract the message type info and
                // any arguments
                Tuple<string, TArg, Exception> arg = (Tuple<string, TArg, Exception>)stateInfo;
                // call all observers that have registered to receive this message type in parallel
                Parallel.ForEach(handlersDict.Keys
                    // find the right dictionary list entry by message type identifier
                    .Where(handlerKey => handlerKey == arg.Item1)
                    // dereference the list entry by message type identifier to get a reference to the observer
                    .Select(handlerKey => handlersDict[handlerKey]), (handlerList, state) =>
                    {
                        lock (_lockPublishing)
                        {
                            List<int> descopedRefIndexes = new List<int>(handlerList.Count);
                            // search the list of references and invoke registered observers
                            foreach (WeakReference weakRef in handlerList)
                            {
                                // try to obtain a strong reference to the target
                                Delegate dlgRef = (weakRef.Target as Delegate);
                                // check if the underlying delegate reference is still valid
                                if (dlgRef != null)
                                {
                                    // yes it is, get the delegate reference via Target property, convert it to Action and invoke the observer
                                    try
                                    {
                                        (dlgRef as Action<TArg>).Invoke(arg.Item2);
                                    }
                                    catch (Exception e)
                                    {
                                        // trouble invoking the target observer's reference, mark it for deletion
                                        descopedRefIndexes.Add(handlerList.IndexOf(weakRef));
                                        Console.WriteLine(String.Format("Error looking up target reference: {0}", e.Message));
                                    }
                                }
                                else
                                {
                                    // the target observer's reference has been descoped, mark it for deletion
                                    descopedRefIndexes.Add(handlerList.IndexOf(weakRef));
                                    Console.WriteLine(String.Format("Message type \"{0}\" has been unsubscribed from.", arg.Item1));
                                    MessageBus.IamDone = true;
                                }
                            }
                            // remove any descoped references
                            descopedRefIndexes.ForEach(index => handlerList.RemoveAt(index));
                        }
                    });
            }
            // catch all Exceptions
            catch (AggregateException e)
            {
                Console.WriteLine(String.Format("Error dispatching messages: {0}", e.Message));
            }
        }

        /// <summary>
        /// Subscribes the specified delegate to handle messages of type TMessage
        /// </summary>
        /// <typeparam name="TArg">Message argument type</typeparam>
        /// <param name="action">WeakReference that represents the handler for this message type to be registered with the bus</param>
        public static void Subscribe<TArg>(Action<TArg> action)
            where TArg : class
        {
            // validate input
            if (action == null)
                throw new ArgumentNullException(String.Format("Error subscribing to message type \"{0}\": Specified action reference is null.", typeof(TArg)));
            // build the queue work item key identifier
            string key = typeof(TArg).ToString();
            // check if a message of this type was already added to the bus
            if (!handlersDict.ContainsKey(key))
            {
                // no, it was not, create a new dictionary entry and add the new observer's reference to it
                List<WeakReference> newHandlerList = new List<WeakReference>();
                handlersDict.TryAdd(key, newHandlerList);
            }
            lock (_lockSubscribing)
            {
                // append this new observer's reference to the list, if it does not exist already
                if (!handlersDict[key].Any(existing => (existing.Target as Delegate) != null && (existing.Target as Delegate).Equals(action)))
                {
                    // append the new reference
                    handlersDict[key].Add(new WeakReference(action, true));
                }
            }
        }
    }
}
使用系统;
使用System.Collections.Concurrent;
使用System.Collections.Generic;
使用System.Linq;
使用System.Linq.Expressions;
使用系统线程;
使用System.Threading.Tasks;
名称空间测试
{
班级计划
{
静态void Main(字符串[]参数)
{
Console.WriteLine(“启动应用程序”);
测试=新测试();
//取消对这些行的注释会导致自动取消订阅Message1
//test=null;
//GC.Collect();
//GC.WaitForPendingFinalizers();
//在此线程上发布Message1
//Publish(newmessage1());
//在另一个线程上发布Message1
ThreadPool.QueueUserWorkItem(委托
{
Publish(newmessage1());
});
而(!MessageBus.IamDone)
{
睡眠(100);
}
Console.WriteLine(“退出应用程序”);
Console.WriteLine(“按下以终止程序”);
Console.WriteLine();
Console.ReadLine();
}
}
公开课考试
{
公开考试()
{
Console.WriteLine(“订阅消息1”);
订阅(OnMessage1);
Console.WriteLine(“订阅消息2”);
订阅(OnMessage2);
}
消息1上的公共无效(消息1消息)
{
Console.WriteLine(“获取消息1.发布消息2”);
Publish(newmessage2());
}
消息2上的公共无效(消息2消息)
{
Console.WriteLine(“获取消息2.关闭应用程序”);
MessageBus.IamDone=true;
}
}
公共抽象类消息库
{
公共字符串消息;
}
公共类Message1:MessageBase
{
}
公共类Message2:MessageBase
{
}
公共静态类消息总线
{
//这纯粹是为了这个测试
公共静态bool IamDone=false;
/////////////////////////////////////
/// 
///按消息类型列出消息处理程序的字典
/// 
私有静态ConcurrentDictionary handlersDict=新ConcurrentDictionary();
/// 
///用于发布调用的线程同步对象
/// 
私有静态对象_lockPublishing=新对象();
/// 
///用于订阅调用的线程同步对象
/// 
私有静态对象_locksubling=新对象();
/// 
///创建封装提供的参数化消息的工作队列项
///并发送它。
/// 
///消息参数类型
///消息参数
公共静态无效发布(TMessage消息)
其中TMessage:MessageBase
{
//创建字典键
string key=string.Empty;
key=typeof(TMessage).ToString();
//将队列工作项参数初始化为字典类型键和消息参数的元组
元组参数=新元组(键、消息、空);
//将消息推送到工作队列上
QueueUserWorkItem(新的WaitCallback(_PublishMessage),参数);
}
/// 
///将消息发布到总线,从而在适当时调用观察器。
/// 
///消息参数类型
///队列工作项参数
私有静态void_PublishMessage(对象状态信息)
TArg:在哪里上课
{
尝试
{
//转换队列工作项参数以提取消息类型信息和
//有什么论据吗
元组arg=(元组)stateInfo;
//调用所有已注册以并行接收此消息类型的观察者
Parallel.ForEach(把手钥匙
//按消息类型标识符查找正确的字典列表项
public class MessageBus : Singleton<MessageBus> // Singleton<> is my library class
public static class MessageBus