- Home /
The question is answered, right answer was accepted. If you have more questions, ask a seperate question
Unity3D and C# - Coroutines vs threading
I'm developing a terrain generation system. The terrain is split up into smaller chunks which can be loaded and deloaded.
The problem is that the generation of the chunks can be a bit on the heavy side.
This can cause small noticeable lags as you walk around.
My first thought was to add threading support by doing all heavy calculations in a thread
(Since I know the Unity3D API is not thread-safe I kept all Unity related objects and calls out of the threading).
Problem is I can't find any way to invoke the thread method back to the main thread of Unity.
My second option it seems is to look into coroutines. From what I understand coroutines allows you to break from a method and return at a later frame to finish it.
(But would coroutines actually be effective for my situation then? For example, if I split the generation method up into 4 frames, would users with fast computers will be punished if their CPU's could have squeezed more per frame, or have I misunderstood something?)
I would love to hear your suggestions.
Very helpful answer and nice to see someone go through the trouble of filling in a complete usable base class for someone to use.
I have a related question though and it sounds like Coroutines will be the only way to go if you cannot access Unity API calls such as Instantiate(). I have a feature where the level expands as you play spawning prefab "rooms" while the player is playing. This creates large noticable frame spikes as each room is instantiated.
Is there some documentation somewhere that outlines which Unity API calls are thread safe and which are not?
Absolutely no Unity API calls are thread safe. Unity is single-threaded and that's that.
That's not really true. You can use certain things like Debug.Log in a thread. All of the $$anonymous$$athf functions are fine, though you could debate whether that's "Unity API" or just $$anonymous$$ono, since they're mostly just wrappers for System.$$anonymous$$ath functions. In any case, if something is not thread-safe, Unity actively prevents you from using it, so there's no worry about accidentally using something in threads that you shouldn't.
Hi @PastryGood fwiw in relation to your question about splitting a task over frames (in Update/coroutine) You just watch the time and give up until the next frame when you've spent too long on it. I put in an answer below to save someone some typing.
Answer by Bunny83 · Nov 30, 2012 at 08:11 PM
That's quite simple ;) You should use a class which represents a threading job. This class will be initialized by the Unity main thread. Then you start a worker thread on a function of that class and let it do it's job.
Here's the important part: Of course you store this job somewhere on the Unity side and have the Update loop (or a coroutine) continously checking an "isDone" variable in your class which will be set by the worker thread when it's finished and when it stored all data it produced. It's important that you make the access to this isDone boolean thread safe since both threads will need to access it.
edit
I just took a closer look at my first (untested) example. Unfortunately you can't use lock on value types like a bool, so you need any kind of reference type as lock handle. I've made a small "framework" for a threaded job:
public class ThreadedJob
{
private bool m_IsDone = false;
private object m_Handle = new object();
private System.Threading.Thread m_Thread = null;
public bool IsDone
{
get
{
bool tmp;
lock (m_Handle)
{
tmp = m_IsDone;
}
return tmp;
}
set
{
lock (m_Handle)
{
m_IsDone = value;
}
}
}
public virtual void Start()
{
m_Thread = new System.Threading.Thread(Run);
m_Thread.Start();
}
public virtual void Abort()
{
m_Thread.Abort();
}
protected virtual void ThreadFunction() { }
protected virtual void OnFinished() { }
public virtual bool Update()
{
if (IsDone)
{
OnFinished();
return true;
}
return false;
}
public IEnumerator WaitFor()
{
while(!Update())
{
yield return null;
}
}
private void Run()
{
ThreadFunction();
IsDone = true;
}
}
This would serve as base class. Just create a concrete Job class which overrides the ThreadFunction function and implement your code. Here's an example:
public class Job : ThreadedJob
{
public Vector3[] InData; // arbitary job data
public Vector3[] OutData; // arbitary job data
protected override void ThreadFunction()
{
// Do your threaded task. DON'T use the Unity API here
for (int i = 0; i < 100000000; i++)
{
OutData[i % OutData.Length] += InData[(i+1) % InData.Length];
}
}
protected override void OnFinished()
{
// This is executed by the Unity main thread when the job is finished
for (int i = 0; i < InData.Length; i++)
{
Debug.Log("Results(" + i + "): " + InData[i]);
}
}
}
This is how you would use the job class:
Job myJob;
void Start ()
{
myJob = new Job();
myJob.InData = new Vector3[10];
myJob.Start(); // Don't touch any data in the job class after you called Start until IsDone is true.
}
void Update()
{
if (myJob != null)
{
if (myJob.Update())
{
// Alternative to the OnFinished callback
myJob = null;
}
}
}
edit
I just added the WaitFor coroutine which allows you to easily wait in a coroutine for the thread to finish. Just use
yield return StartCoroutine(myJob.WaitFor());
inside another coroutine. This can be used instead of calling Update manually each frame.
Btw my solution is for a few really heavy tasks. If you have a lot small tasks, it's probably better to use a queue and one or multiple worker threads that run permanently and pick jobs from the queue. If there's no job they sleep for some time. Oh and if you use this, don't create more workerthreads as you have (free) cpu cores ;)
Usually to get most out of the threading you would bound each thread to a certain core, but i guess that's don't necessary since we just want to reduce hicups in the Unity main thread.
Just wanted to say thanks @Bunny83 , this helped a lot.
+5000 Upvotes (or at least the one I can give, anyway)
@thrmotta:
I just edited my answer and included a WaitFor coroutine. I've written this on my tablet, so I can't write a proper example right now ^^
can use a volatile IsDone rather than locking for efficiency. and WaitFor() needs to be public to use the nested coroutine code you posted.
Answer by Eric5h5 · Nov 30, 2012 at 08:16 PM
Coroutines aren't really appropriate for this. Everything is still run in the main thread in that case, and there's no effective way of distributing CPU usage, which is especially important considering that nearly everything these days has at least two CPU cores.
I see :) Well coroutines are off the list then. I think it is generally kind of odd that Unity3D as the commercial engine it is does not make a (what in some cases) vital feature such as threading easily integrate into the engine, such a shame.
How much easier did you want it to be? All the code is above. All threaded program$$anonymous$$g is incredibly difficult, on all platforms and environments.
I know this is old, but thought I would ask in case Eric5h5 is still around - I think coroutines would work fine if your ONLY goal is to reduce hiccups in performance. Threading is probably the better choice because otherwise it will actually increase performance (your main game runs on the main thread, and the new stuff is built concurrently, as opposed to using the main thread to build the new stuff); otherwise, a coroutine which builds the new terrain but yields based on how much time has passed in the frame could also reduce the hiccups, could it not? A potential pitfall is that if your game is experiencing low FPS your new terrain might not be built in a timely fashion, but if you're having performance trouble you will need the extra performance from multithreading anyway. As long as your FPS is decent, coroutines actually ARE an effective way of distributing CPU usage.
Answer by petersvp · Apr 23, 2015 at 01:38 PM
Let me ad my two cents as well. I am also making Minecraft-inspired voxel game but with smooth terrain. After various bench, I ended up implementing my entire voxels stuff directly in C++ with PolyVox, as a DLL. This way, the generation is lighting fast. C++'s DLL uses worker threads, the Boss-and-Employees pattern styles. It exposes C functions so Unity can request loading/unloading of chunks, checking for results and fetching meshes. A C# coroutine constantly polls the C++ library for "Job Results" each frame. If there is a mesh generated, this coroutine "imports" the mesh. The process is not so complicated, the threading model was more chalenging than sending of the meshes to Unity. I basically create byte array with vertex, index and splatting buffers, then in Unity generate the mesh and splatting maps.
Do you think the speed-up was due to heavy calculations made in C++ (PolyVox) or did the threading model itself also contribute? I'm asking because I'm looking for an alternative to TLP Dataflow that requires .NET 4.5 (Unity is .NET 3.5) and was thinking to implement an multi-threaded actor library myself in C++. But I don't know if it's worth the effort in comparison with implementing the actor model using System.Threading in C#.
Answer by GGeff · Feb 16, 2016 at 01:12 PM
Can use a volatile IsDone rather than locking for efficiency.
I would never recommend such a thing.
Reasons outlined here: http://www.albahari.com/threading/part4.aspx#_The_volatile_keyword
and here: http://stackoverflow.com/questions/154551/volatile-vs-interlocked-vs-lock
It's way too risky, you have to remember never to do read-modify-write on the writer thread. Ins$$anonymous$$d, for such micro-efficiency it would be better to use Interlocked. https://msdn.microsoft.com/en-us/library/system.threading.interlocked(v=vs.110).aspx
You bring up a very good point - that volatile is no multi-threaded panacea. However, in this particular case I am suggesting removing entirely the getter and setter, and making a simple bool volatile. There won't ever be a chance in this application for a read-modify-write. The bool isDone is only ever read in one thread and written (with no read or modify) in another thread, so it should be quite safe if I've read your links properly.
I agree this case seems like one of those where using volatile would be justified.
Researching recently into this topic myself, I've found some confusion and misunderstanding about the true value of using it, even outside Unity circles. I wanted to post the links above to make sure people don't take it lightly and compile a solid reference about it.
While it's certainly a viable option, I would rather recommend using Interlocked like in $$anonymous$$SDN example above - seems more resistant to code change, that's all.
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
Illuminating a 3D object's edges OnMouseOver (script in c#)? 1 Answer
Delegates and "__ Can only be called from main thread" 2 Answers
Sprinting camera shake 1 Answer