[SOLVED] Shortest path THREAD blocks main THREAD of Unity

Hi guys,

Because i have a lot of nodes, instead of StartCoroutine(), i am trying to use Thread.
I create a Thread for finding shortest path, but seems it blocks the main thread of unity. I said this, because I can’t move my object until my thread finish his job.
Can you help me?
Here is my code:
4384039--398029--upload_2019-4-2_16-34-58.png

DRAWPATH

   private void Start()
    {
        PathRequestManager.RequestPath(new PathRequest(StartNode, EndNode, OnPathFound));
    }

    public void OnPathFound(List<Waypoint> waypoints, bool pathSuccessful)
    {
        if (pathSuccessful)
        {
            path = new List<Waypoint>(waypoints);
            Line = GetComponent<LineRenderer>();
            Line.transform.position = this.transform.position;
            Line.positionCount = path.Count;

            for (int i = 0; i < path.Count; ++i)
            {
                Line.SetPosition(i, path[i].transform.position);
            }
        }
    }

    private void FixedUpdate()
    {
        Dijkstra();
    }

    private void Dijkstra()
    {
        if ((path.Count > 0) && (GetDistance(path[0].transform.position) < 10))
        {
            path.Remove(path[0]);
            PathRequestManager.RequestPath(new PathRequest(path[0], EndNode, OnPathFound));
        }
    }

PATH REQUEST MANAGER

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Threading;

public class PathRequestManager : MonoBehaviour
{

    Queue<PathResult> results = new Queue<PathResult>();

    static PathRequestManager instance;
    PathFinding pathFinding;

    void Awake()
    {
        instance = this;
        pathFinding = GetComponent<PathFinding>();
    }

    void Update()
    {
        if (results.Count > 0)
        {
            int itemsInQueue = results.Count;
            lock (results)
            {
                for (int i = 0; i < itemsInQueue; i++)
                {
                    PathResult result = results.Dequeue();
                    result.callback(result.path, result.success);
                }
            }
        }
    }

    public static void RequestPath(PathRequest request)
    {
        ThreadStart threadStart = delegate {
            instance.pathFinding.FindPath(request, instance.FinishedProcessingPath);
        };
        threadStart.Invoke();
    }

    public void FinishedProcessingPath(PathResult result)
    {
        lock (results)
        {
            results.Enqueue(result);
        }
    }
}

public struct PathResult
{
    public List<Waypoint> path;
    public bool success;
    public Action<List<Waypoint>, bool> callback;

    public PathResult(List<Waypoint> path, bool success, Action<List<Waypoint>, bool> callback)
    {
        this.path = path;
        this.success = success;
        this.callback = callback;
    }

}

public struct PathRequest
{
    public Waypoint pathStart;
    public Waypoint pathEnd;
    public Action<List<Waypoint>, bool> callback;

    public PathRequest(Waypoint start, Waypoint end, Action<List<Waypoint>, bool> callback)
    {
        this.pathStart = start;
        this.pathEnd = end;
        this.callback = callback;
    }

}

PATHFINDING

using UnityEngine;
using System.Collections.Generic;
using System.Diagnostics;
using System;
using System.Linq;

public class PathFinding : MonoBehaviour
{

    public GameObject Cluster;

    public static List<Waypoint> Graph;

    private void Awake()
    {
        Graph = new List<Waypoint>();
        foreach (Transform child in Cluster.transform)
        {
            Graph.Add(child.gameObject.GetComponent<Waypoint>());
        }
    }

    public void FindPath(PathRequest request, Action<PathResult> callback)
    {
        Vector3[] waypoints = new Vector3[0];

        Waypoint startNode = request.pathStart;
        Waypoint targetNode = request.pathEnd;

        List<Waypoint> shortestPath = Dijsktra(startNode, targetNode);
        callback(new PathResult(shortestPath, true, request.callback));

    }

    public static List<Waypoint> Dijsktra(Waypoint start, Waypoint end)
    {
        // We don't accept null arguments
        if (start == null || end == null)
            return null;

        // The final path
        List<Waypoint> path = new List<Waypoint>();

        // If the start and end are same node, we can return the start node
        if (start == end)
        {
            path.Add(start);
            return path;
        }

        // The list of unvisited nodes
        List<Waypoint> unvisited = new List<Waypoint>();

        // Previous nodes in optimal path from source
        Dictionary<Waypoint, Waypoint> previous = new Dictionary<Waypoint, Waypoint>();

        // The calculated distances, set all to Infinity at start, except the start Node
        Dictionary<Waypoint, float> distances = new Dictionary<Waypoint, float>();

        for (int i = 0; i < Graph.Count; i++)
        {
            Waypoint node;
            node = Graph[i];
            unvisited.Add(node);

            // Setting the node distance to Infinity
            distances.Add(node, float.MaxValue);
        }

        // Set the starting Node distance to zero
        distances[start] = 0f;

        while (unvisited.Count != 0)
        {
            // Ordering the unvisited list by distance, smallest distance at start and largest at end
            unvisited = unvisited.OrderBy(node => distances[node]).ToList();

            // Getting the Node with smallest distance
            Waypoint current = unvisited[0];

            // Remove the current node from unvisisted list
            unvisited.Remove(current);

            // When the current node is equal to the end node, then we can break and return the path
            if (current == end)
            {
                // Construct the shortest path
                while (previous.ContainsKey(current))
                {
                    // Insert the node onto the final result
                    path.Insert(0, current);

                    // Traverse from start to end
                    current = previous[current];
                }

                // Insert the source onto the final result
                path.Insert(0, current);
                break;
            }

            // Looping through the Node connections (neighbors) and where the connection (neighbor) is available at unvisited list
            for (int i = 0; i < current.nodes.Count; i++)
            {

                Waypoint neighbor = current.nodes[i].waypoint;

                // Getting the distance between the current node and the connection (neighbor)
                float length = current.nodes[i].Distance;

                // The distance from start node to this connection (neighbor) of current node
                float fullLength = distances[current] + length;

                // A shorter path to the connection (neighbor) has been found
                if (fullLength < distances[neighbor])
                {
                    distances[neighbor] = fullLength;
                    previous[neighbor] = current;
                }
            }
        }
        return path;
    }
}

I can’t see where do you create new Thread?

1 Like

threadStart.Invoke() is invoking the delegate on the main thread. You need to create a new thread and call that thread’s Start method to run the delegate on a separate thread.

Thread thread = new Thread(threadStart);
thread.Start();

Note that you will have to keep a reference to that thread and manage its lifetime on your own. There is bookkeeping involved with threads.

1 Like

Good catch. Why does he have to manage the thread? His code’s a bit subtle, but it appears to me that it will exit on its own.

I changed it, but now, when thread starts i get low fps…

ThreadStart threadStart = delegate {
            instance.pathFinding.FindPath(request, instance.FinishedProcessingPath);
        };
        Thread thread = new Thread(threadStart);
        thread.Start();

EDIT: I think the problem is when more than 2 points are very close. Then, I start two threads and i have low fps.

I notice you have 2 threads (forum threads, not code threads…) about this exact topic. Maybe try to stay in one less people get confused about the entire context:

Sorry,
Can I delete one of them?

This topic is solved> SOLUTIN:

 public static void RequestPath(PathRequest request)
    {
        ThreadStart threadStart = delegate {
            instance.pathFinding.FindPath(request, instance.FinishedProcessingPath);
        };
        Thread thread = new Thread(threadStart);
        thread.Start();
    }