- Home /
Multi-threading safely
So im currently working with making a networking solution for unity using .net sockets and so far its not to hard and ive gotten most of the basic jazz done and I do use threading currently but to use the Unity API in the main thread I had to make a task manager but its not threading so each task is one after another as you would think. So I wondered is there a way to make it thread in some way? I thought of making it so the task manager used coroutines but im not sure how to go about it for some reason and i might have low understanding of how they work.
here are some of my scripts:
Here is the container for the Task Manager that is needed and that makes the task that then goes into the queue of tasks
public class TaskContainer {
static TaskContainer container = null;
public Queue<Task> TaskQueue = new Queue<Task>();
public object _queueLock = new object();
public TaskContainer(){
TaskQueue = new Queue<Task> ();
_queueLock = new object ();
}
public void ScheduleTask(Task newTask){
try{
lock (_queueLock) {
if(TaskQueue.Count < 1000)
{
Debug.Log(TaskQueue.Count);
TaskQueue.Enqueue(newTask);
}
}
}catch(ThreadInterruptedException e){
Debug.Log(e);
}
}
public static TaskContainer GetInstance(){
if (container == null) {
container = new TaskContainer();
}
return container;
}
}
This is the actual tasker that does it
public delegate void Task();
public class TaskManager : MonoBehaviour {
TaskContainer container = null;
void Start()
{
container = TaskContainer.GetInstance ();
}
// Update is called once per frame
void Update () {
if (container != null) {
lock (container._queueLock) {
if (container.TaskQueue.Count > 0) {
container.TaskQueue.Dequeue () ();
}
}
}
}
}
and here is an example of one of the functions that calls those and is needed to be done thread like
TaskContainer container = TaskContainer.GetInstance ();
public void serversinstantiate(string prefabName, string x, string y, string z, string actorId, string clientId)
{
float px = float.Parse (x, CultureInfo.InvariantCulture.NumberFormat);
float py = float.Parse (y, CultureInfo.InvariantCulture.NumberFormat);
float pz = float.Parse (z, CultureInfo.InvariantCulture.NumberFormat);
int id = System.Convert.ToInt32 (actorId);
int cliId = System.Convert.ToInt32 (clientId);
Debug.Log ("Intantiate this: " + prefabName + " at this place: " + px + " " + py + " " + pz);
if (container != null) {
Debug.Log("in if");
container.ScheduleTask (new Task (delegate {
Debug.Log ("in task " + prefabName);
GameObject newObject = (GameObject)GameObject.Instantiate (Resources.Load (prefabName), new Vector3 (px, py, pz), Quaternion.identity);
newObject.GetComponent<AltimitView>().actorId = id;
if(AltimitNetowrk.ClientId == cliId){
newObject.GetComponent<AltimitView>().isMine = true;
}
GetComponent<NetworkManager>().NetowrkObjects.Add(newObject);
}));
}
}
Unity API is not thread safe, so you can not thread anything using it.
Coroutines are not threads, but let you balance out performance by perfor$$anonymous$$g calculations over several frames.
I understand that but that might be how i have to do it unless there is another way to update multiple transforms positions potentially at the same time or i do magic and just do some animation as if its always updating over time.
Answer by Bunny83 · Sep 01, 2015 at 05:56 PM
Your example "task" doesn't seem to contain anything that would be close to be worth of "threading".
I guess your problem is mainly the way your "task scheduler" is implemented. It always processes only one task per frame. So if you generate 1000 tasks and your game runs at 60 fps it takes about 17 seconds to process the whole list. That's actually a bad implementation ^^. Usually you can execute far more tasks per frame. The usual implementation simply processes all pending tasks at once. However if the tasks are quite demanding you might want to implement a watchdog.
But first of all the way you pop a task is horrible inefficient ^^. You lock your queue and keep it locked until your task is actually finished. You only have to lock it for the actual dequeuing.
It should look something like that:
Task DequeueTask()
{
if (container == null)
return null;
Task task = null;
lock (container._queueLock)
{
if (container.TaskQueue.Count > 0) {
task = container.TaskQueue.Dequeue ();
}
}
return task;
}
void Update ()
{
Task current = DequeueTask();
float timeout = Time.realtimeSinceStartup + 0.04f;
while(current != null)
{
current();
if (Time.realtimeSinceStartup > timeout)
break;
current = DequeueTask();
}
}
The 0.04s equals about 25 fps. however this only takes the time into account that the tasks need to process and not the rest of your game. This scheduler will schedule at least one task per frame even when the frame rate has already dropped below 25fps. Furthermore it will schedule as many task as possible within one frame before hitting the time cap.
Time.realtimeSinceStartup is a quite unprecise time, but it should be enough for this case. You can instead use a Stopwatch or High performance timer. Just in case: You can't use Time.time since that will not advance during a frame. So if you have an never ending amount of tasks it will never terminate since Time.time is also stuck. Though that case should never happen as that means your task queue is filling faster than you can process it ^^.
Your answer
