两个相同的多线程脚本导致内存泄漏

时间:2017-10-10 13:05:09

标签: c# multithreading unity3d

我正在使用自己的多线程来处理我的算法独立路径寻找统一。但是,当我执行两个相同的类时,我得到内存泄漏,当只执行一个实例时,我有没有问题。如果有必要,我真的想要至少使用两个线程。

以下是我遇到的课程。请记住,两个独立的线程必须执行此脚本的一部分。 AddJob可以从主统一线程调用,但最有可能从代理的另一个更新线程调用。

namespace Plugins.PathFinding.Threading
{
    internal class PathFindingThread
    {

        private Thread m_Worker;

        private volatile Queue<CompletedProcessingCallback> m_CallbackQueue;
        private volatile Queue<IAlgorithm> m_QueuedTasks;

        internal int GetTaskCount
        {
            get
            {
                return m_QueuedTasks.Count;
            }
        }

        internal PathFindingThread()
        {
            m_Worker = new Thread(Run);
            m_CallbackQueue = new Queue<CompletedProcessingCallback>(); 
            m_QueuedTasks = new Queue<IAlgorithm>();
        }

        private void Run()
        {
            Debug.Log("<b><color=green> [ThreadInfo]:</color></b> PathFinding Thread Started ");
            try
            {
                while(true)
                {
                    if (m_QueuedTasks.Count > 0)
                    {
                        IAlgorithm RunningTask = m_QueuedTasks.Dequeue();
                        RunningTask.FindPath(new IAlgorithmCompleted(AddCallback));
                    }
                    else
                        break;
                }

                Debug.Log("<b><color=red> [ThreadInfo]:</color></b> PathFinding Worker is idle and has been Stopped");

            }
            catch(Exception)
            {
                Debug.Log("<b><color=red> [ThreadInfo]:</color></b> PathFinding thread encountred an error and has been aborted");
            }
        }

        internal void AddJob(IAlgorithm AlgorithmToRun)
        {
            m_QueuedTasks.Enqueue(AlgorithmToRun);
            //Debug.Log("Added Job To Queue"); 
        }

        private void AddCallback(CompletedProcessingCallback callback)
        {
            m_CallbackQueue.Enqueue(callback);
        }

        private void Update()
        {
            if (m_CallbackQueue.Count > 0)
            {
                if (m_CallbackQueue.Peek().m_Callback != null) { }
                    m_CallbackQueue.Peek().m_Callback.Invoke(m_CallbackQueue.Peek().m_Path);
                m_CallbackQueue.Dequeue();
            }

            if (m_Worker.ThreadState != ThreadState.Running && m_QueuedTasks.Count != 0)
            {
                m_Worker = new Thread(Run);
                m_Worker.Start();
            }

        }
    }

    internal delegate void IAlgorithmCompleted(CompletedProcessingCallback callback);

    internal struct CompletedProcessingCallback
    {
        internal volatile FindPathCompleteCallback m_Callback;
        internal volatile List<GridNode> m_Path;
    }
}


namespace Plugins.PathFinding
{
    internal enum TypeOfNode
    {
        Ground,
        Air
    }

    //used to store location information since array can only take rounded numbers
    internal struct Position
    {
        internal int x;
        internal int y;
        internal int z;
    }

    internal class GridNode
    {
        internal Position M_PostitionInGrid { get; private set; }
        internal Vector3 M_PostitionInWorld { get; private set; }

        internal TypeOfNode M_type { get; private set; }

        internal bool m_IsWalkable = true;

        internal GridNode m_ParrentNode;

        internal int Hcost;
        internal int Gcost;

        internal int Fcost { get { return Hcost + Gcost; } }

        internal GridNode(Position postion , Vector3 WorldPosition)
        {
            M_PostitionInGrid = postion;
            m_IsWalkable = true;
            M_PostitionInWorld = WorldPosition;
        }
    }
}
    internal delegate void FindPathCompleteCallback(List<GridNode> Path);

    internal abstract class IAlgorithm
    {
        protected GridNode m_SavedStart;
        protected GridNode m_SavedTarget;

        protected List<GridNode> m_LocatedPath;

        protected FindPathCompleteCallback m_Callback;
        internal FindPathCompleteCallback GetCallback
        {
            get
            {
                return m_Callback;
            }
        }

        protected PathFindingGrid m_grid;

        internal abstract void FindPath(IAlgorithmCompleted callback);

        protected abstract List<GridNode> CreatePath(PathFindingGrid Grid, GridNode Start, GridNode Target);

        protected abstract List<GridNode> RetracePath(GridNode start, GridNode target); 
    }
namespace Plugins.PathFinding.Astar
{

    internal class AstarFinder : IAlgorithm
    {

        //construction of the Algorithm
        internal AstarFinder(GridNode start, GridNode target, FindPathCompleteCallback Callback)
        {
            m_SavedStart = start;
            m_SavedTarget = target;
            m_Callback = Callback;
            m_LocatedPath = new List<GridNode>();
            m_grid = PathFindingGrid.GetInstance;
        }

        //function to start finding a path
        internal override void FindPath(IAlgorithmCompleted callback)
        {

            //running Algorithm and getting the path
            m_LocatedPath = CreatePath(PathFindingGrid.GetInstance, m_SavedStart, m_SavedTarget);

            callback.Invoke(
                new CompletedProcessingCallback()
                {
                    m_Callback = m_Callback,
                    m_Path = m_LocatedPath
                });

        }

        //Algorithm
        protected override List<GridNode> CreatePath(PathFindingGrid Grid, GridNode Start, GridNode Target)
        {
            if(Grid == null ||
                Start == null ||
                Target == null)
            {
                UnityEngine.Debug.Log("Missing Parameter, might be outside of grid");
                return new List<GridNode>();
            }

            List<GridNode> Path = new List<GridNode>();

            List<GridNode> OpenSet = new List<GridNode>();
            List<GridNode> ClosedSet = new List<GridNode>();

            OpenSet.Add(Start);

            int Retry = 0;

            while (OpenSet.Count > 0)
            {
                if(Retry > 3000 || Grid == null)
                {
                    UnityEngine.Debug.Log("Path Inpossible Exiting");
                    break;
                }

                GridNode CurrentNode = OpenSet[0];

                for (int i = 0; i < OpenSet.Count; i++)
                {
                    if(OpenSet[i].Fcost < CurrentNode.Fcost || OpenSet[i].Fcost == CurrentNode.Fcost && OpenSet[i].Hcost < CurrentNode.Hcost)
                    {
                        CurrentNode = OpenSet[i];
                    }
                }

                OpenSet.Remove(CurrentNode);
                ClosedSet.Add(CurrentNode);

                if(CurrentNode == Target)
                {
                    Path = RetracePath(CurrentNode,Start);
                    break;
                }

                GridNode[] neighbour = Grid.GetNeighbouringNodes(CurrentNode);

                for (int i = 0; i < neighbour.Length; i++)
                {
                    if (!neighbour[i].m_IsWalkable || ClosedSet.Contains(neighbour[i]))
                        continue;

                    int CostToNeighbour = CurrentNode.Gcost + Grid.GetDistance(CurrentNode, neighbour[i]);

                    if(CostToNeighbour < neighbour[i].Gcost || !OpenSet.Contains(neighbour[i]))
                    {
                        neighbour[i].Gcost = CostToNeighbour;
                        neighbour[i].Hcost = Grid.GetDistance(neighbour[i], Target);
                        neighbour[i].m_ParrentNode = CurrentNode;

                        if (!OpenSet.Contains(neighbour[i]))
                            OpenSet.Add(neighbour[i]);
                    }
                }

                Retry++;
            }
            return Path;
        }

        //retracing the path out of a node map
        protected override List<GridNode> RetracePath(GridNode start, GridNode target)
        {
            List<GridNode> Output = new List<GridNode>();

            GridNode current = start;

            while(current != target)
            {
                Output.Add(current);
                current = current.m_ParrentNode;
            }

            Output.Reverse();

            return Output;
        }

    }
}

1 个答案:

答案 0 :(得分:0)

这表明代码的核心是线程安全的。

internal class PathFindingThread
    {

        Task m_Worker;

        ConcurrentQueue<CompletedProcessingCallback> m_CallbackQueue;
        ConcurrentQueue<IAlgorithm> m_QueuedTasks;

        internal int GetTaskCount
        {
            get
            {
                return m_QueuedTasks.Count;
            }
        }

        internal PathFindingThread()
        {
            m_CallbackQueue = new ConcurrentQueue<CompletedProcessingCallback>();
            m_QueuedTasks = new ConcurrentQueue<IAlgorithm>();
            m_Worker = Task.Factory.StartNew(() =>
            {
                while (true)
                {
                    IAlgorithm head = null;
                    if (m_QueuedTasks.TryDequeue(out head))
                    {
                        head.FindPath(new IAlgorithmCompleted(AddCallback));
                    }
                    else
                    {
                        Task.Delay(0);
                    }
                }
            });
        }

        internal void AddJob(IAlgorithm AlgorithmToRun)
        {
            m_QueuedTasks.Enqueue(AlgorithmToRun);
        }

        private void AddCallback(CompletedProcessingCallback callback)
        {
            m_CallbackQueue.Enqueue(callback);
        }

        private void Update()
        {
            CompletedProcessingCallback cb = null;
            if (m_CallbackQueue.TryDequeue(out cb))
            {
                cb.m_Callback.Invoke(cb.m_Path);
            }
        }
    }

Volatile仅适用于更改字段的值 - 不调用字段引用的集合上的方法。

你可能不需要在CompletedProcessingCallback中使用Volatile,但它取决于使用它的其他地方。当然在结构域上具有易失性是一种难闻的气味。

首先解决这些线程问题,然后查看是否还有问题。