2017-10-10 65 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,我會看看這個 –