2017-10-10 41 views
0

我正在为自己的多线程工作,为我的算法独立寻路统一。然而,当我执行两个相同的类时,我得到一个内存泄漏,当只执行一个实例时,我有没有问题。如果有必要,我真的想使用至少两个线程。两个相同的多线程脚本导致内存泄漏

以下是我遇到的问题。请记住,两个独立线程必须执行此脚本的部分内容。可以从主要统一线程调用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; 
     } 

    } 
} 
+0

为什么不使用ThreadPool而不是每次创建新的Thread? – Programmer

+0

是否有*不*使用阻塞队列的原因? – Fildor

+1

@Fildor这是Unity3d,引擎每帧调用一次'Update',即使它被标记为私有。 –

回答

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); 
      } 
     } 
    } 

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

您可以不需要在CompletedProcessingCallback中存在易失性,但它取决于在何处使用它。当然,在一个struct字段上有volatile是一种难闻的气味。

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

+0

我不认为这个答案考虑了一个场景,其中所有的工作都被处理了,并且更多的工作被添加了(在'Run'完成之后),我相信这是'Update'中重新开始工作的意图眼睛作为问题的可能来源)。 – JuanR

+0

你是对的 - 这对我来说看起来像是一个可能的原因。因此,通过这个示例代码,我保持工作人员永远运行,因为这更容易成为线程安全的。我很难看出它的意图 - 如果它不符合,我就会纠正。 – mikelegg

+0

谢谢@mikelegg,我会看看这个 –