- Home /
Thread-Safety and Debug.Log
I am using multi-threading in unity for some connection issues, i know that unity API is not thread-safe and I'm taking that into consideration.
All examples about threading in unity use the function group Debug.Log...
, so i assumed that they are thread-safe, but i need need to know how "safe" are they, and if it was thread-safe, it will have of course some kind of synchronization, so I need to know what kind of synchronization does it have, does it force the calling thread to wait for the next frame or end of the frame? if so, wouldn't that make the calling thread wait for many frames if it had many Debug.Log statements?
Answer by Bunny83 · Nov 25, 2015 at 05:56 PM
Debug.Log (as well as LogError, LogWarning, ...) are afaik the only methods that are actually threadsafe. Making a method thread safe doesn't necessarily mean that one thread always have to wait for another. They just have to wait when they try to access a shared resource at the same time. When running inside the editor, the editor itself (which runs on the main thread) will most likely pull pending entries from a shared buffer into a display buffer. So only for that copy operation the main thread might lock the shared resource. Likewise when another thread uses a Debug.Log it would also lock the buffer to add that message. The lock only lasts as long as the thread is using the shared resource. (More about that at the end.)
At runtime there is nothing in the main thread that actually "reads" the Debug.Log messages. So the only thing that can make a thread to wait when calling Debug.Log is another thread that is currently executing a Debug.Log. Since you shouldn't overuse Debug.Log and at runtime it executes quite fast you shouldn't see much of a wait time. I'm not sure how Unity handles the logfile writing. It might be done once during the gamecycle on the main thread or they might have a seperate thread for that.
It seems that Unity actually treats Debug.Log calls from the main thread differently than those from other threads. That's why we have the events Application.logMessageReceived
and Application.logMessageReceivedThreaded
. Unfortunately since they turned the callback into an actual event (before we had to call Application.RegisterLogCallback(callback)
) it seems they somehow forgot / dropped the documentation of those ^^.
logMessageReceived is executed on the main thread but it only receives messages from Debug.Log calls that has been executed on the main thread. logMessageReceivedThreaded on the other hand will be called for all Debug.Log calls, but from their respective threads, so you have to do any synchronisations yourself if you use that callback.
That's pretty much all i know about Debug.Log at the moment. I haven't done extensive tests with threads and Debug.Log.
Some additional info about locks:
When we talk about syncronisation, a thread usually don't wait for another thread to do something but it might has to wait until another thread has released a lock or has finished completely (joining with the thread).
A usual lock could be divided into 3 parts:
lock(someObject) // part1
{
// part2
}
// part3
part1 will try to aquire a lock for the given object. If another thread currently has a lock active on that object, the thread will wait on this line until the lock is release.
part2 this is the "threadsafe area". Here you can access a shared resource since you can ensure that you are the only one inside that safe area that belongs to the "someObject".
part3 at this point our thread has released the lock and another thread (that might have waited on the lock statement already) now might have taken the lock.
The key for effectiveness is to keep the time a thread holds a lock as short as possible and if possible, avoid aquiring the lock in the first place. This can be done by using a simple volatile flag that a "producer" sets when he has placed data into the buffer. This flag can be read by another thread without using a lock. It can be used to avoid locking an object in a "consumer" thread if there's no new data.
For example this is a bad example of a polling thread:
while (true)
{
lock(lockHandle)
{
// check if data available
}
}
This is bad because of the ratio between the time inside and outside the lock. Since you do nothing outside the lock the thread will have the lock probably 99% of the time. It's also bad since this thread will run at "maximum speed" and causes the core it runs on to have 99.9% usage, even it does almost nothing. A simple Thread.Sleep would fix that.
thanks for the reply, i noticed it one month late because i asked the question like 7 months ago.
now i can say that i used it and i didn't see encounter any problems, the goal of my question was to find any official statement about it.
and for the synchronization part, i know that it's bad design and Unity is highly unlikely to be like that, but i've seen some engines unnecessarily lock some resources before the update and not releasing them until the end of rendering.
Answer by Numid · Nov 27, 2015 at 12:53 PM
void Start()
{
(new Thread(() => {
new GameObject();
})).Start();
}
Raises an exception.
void Start()
{
(new Thread(() => {
Debug.Log("A message");
})).Start();
}
Does not raise any exception.
It might mean that Debug.Log
is thread safe. However this feature is not documented. You can not rely on it.
Your answer
Follow this Question
Related Questions
Multithreaded Input Detection 2 Answers
Instantiating many objects during update lags 1 Answer
Having issues when multithreading and deserializing JSON with JSONUtility 1 Answer
Call long running AI function without tying up game loop? 1 Answer
3D voxel Planet (dual contouring) Job System or Task Parallel Library? 1 Answer