2017-10-10 16 views
0

私は、自分のアルゴリズムに独立性のある単一性の探索のために自分自身のマルチスレッドに取り組んでいます。しかし、同じクラスを2つ実行しているときにメモリリークが発生し、1つのインスタンスのみを実行すると、にはという問題があります。私は本当に少なくとも2つのスレッドを使用する必要がある場合。2つの同一のマルチスレッドスクリプトがメモリリークの原因となっています

以下は、私が問題があるクラスです。 2つの独立したスレッドがこのスクリプトの一部を実行する必要があることに注意してください。 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」を使用してみませんか? – 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に揮発性を持つ必要はありませんが、それ以外はどこで使用されるかによって異なります。確かに構造体フィールドに揮発性があることは悪い匂いです。

これらのスレッドの問題を最初に解決し、問題がまだ解決しているかどうかを確認してください。

+0

この回答は、すべてのジョブが処理され、さらに多くが追加された( 'Run'が完了した後)、' Update'でジョブを再開する意図が何であったかを考えていないと思います問題の原因となる可能性のある目として)。 – JuanR

+0

あなたは正しい - これは私の考えられる原因のように見えます。だから、このサンプルコードでは、私はワーカーを永遠に実行し続けました。これはスレッドセーフである方が簡単でした。その意図を見るのは一生懸命です。もしそれが満たされなければ、私は訂正します。 – mikelegg

+0

ありがとう@mikelegg、私はこれを調べます –

関連する問題