i am not sure how to handle this Problem. Im very new to threading, and also never used Croutines in Unity.
I have a Method wich is pretty expensive in a .dll that i call in an Update() every half a second. This makes my Screen lag for 50ms two times per second, wich is unacceptable.
SomeOtherWork is dependent on the Outputs of SomeExpensiveWork.
My idea was to put âSomeExpensiveWork()â in a thread, and make âDoSomeWork()â a thread wich yields as long as the Thread is still running. I assume the Expensive work is threadsafe for this,
I somehow think this is the wrong Approach even tho it sounds right in the Idea.
In which case, yeah, thatâs a completely suitable way to deal with it.
Some people have even created little libraries that allow the coroutine to jump back and forth between its own thread and the main thread. Like this one here called ThreadNinja:
Yes, if you prefer using a more self-made approach or need to dive into how something like this would work, use object locks to make sure your threads and coroutines are in sync. For example
using System.Collections;
using System.Threading;
using UnityEngine;
public class Test : MonoBehaviour
{
private readonly object threadLock = new object();
private bool workReady = false;
private bool threadShutdown = false;
private int simulatedWork = 0;
private void OnEnable()
{
workReady = false;
threadShutdown = false;
Thread workerThread = new Thread(DoThreadedWork);
workerThread.Start();
StartCoroutine(WaitForThreadWork());
}
private void OnDisable()
{
threadShutdown = true;
}
private IEnumerator WaitForThreadWork()
{
while (!threadShutdown)
{
// wait for the threaded work to complete
while (!workReady)
yield return null;
// assuming this needs to happen on the main thread
// don't let the thread edit our data while we process
lock (threadLock)
{
//SomeOtherWork(Count, ids, vecs1, vecs2, vecs3);
Debug.LogFormat("Simulating some other work {0}", simulatedWork);
workReady = false;
}
}
}
private void DoThreadedWork()
{
while (!threadShutdown)
{
lock (threadLock)
{
//SomeExpensiveWork(ref Count, ref ids, ref vecs1, ref vecs2, ref vecs3);
Thread.Sleep(50); // simulating expensive work for 50ms
Interlocked.Increment(ref simulatedWork);
workReady = true;
}
// wait half a second to do work again
Thread.Sleep(500);
}
}
}
Thereâs lot of ways to deal with the problem. And since I donât have specifics about your problem, I could say exactly what the âusualâ would be⌠nor what I would even do.
But the job system has a very specific use case. And it usually doesnât really consist of calling outside dllâs. So it probably wouldnât meet your needs.
But again, who knows, maybe it does. Maybe whatever your external dll does could be refactored into your local domain and written as a job.
@lordofduct i was sitting for a day learning the new Job System but it seems to me that this is not Fitting my Problem since i work with references, and Jobs dont like references. Also big thanks for showing me the asset âThread ninjaâ, seems very interesting and Fitting for my Needs.
@jschieck thank u for your example. i guess i cant get around using object locks since i call the root method every 0.5 sek. I Need a bit more time to read into your example Code tho.
Iâm wondering why you are calling something so often that is so slow.
Just to check - is this a DLL where you donât have access to the code? Also does this DLL contain non-Unity code and is, therefore, safe to run on another thread?
Could you not write a C# manager to run on itâs own thread that would call the DLL regularly but offer a simple lightweight interface for your Unity code to call? Then it would act to decouple your components. If you later find a more efficient implementation of this DLL method, replacing it would be straightforward to do.
@Doug_B your second Approach makes a lot of sense in my case. I am not sure if it is easy to do like that, since i have not just one Class, but a few that work together and rely on each other.
but how i could imagine it would be that a Manager runs on its own Thread and everytime it is finished with a process, it invokes an Event on the main thread.
Regarding the .dll, i constantly scan the enviroment, for that i have an OpenGL plugin attached that Scans that what i see.
If these other classes are your own Unity ones, you could consider using a Dependency Injection approach on the DLL Managerâs interface.
That would work. Or have a shared, thread safe, memory area that the DLL Manager populates with latest results and then sets a flag to say the data is ready for reading. Your Unity code could then just poll the is-ready-flag.
Maybe it is interesting, here is a script I used with switching threads in a coroutine. Maybe that can work too for oyur problem.
Edit: Sorry In hindsight there is more in the background (which I didnât write myself). There is a whole Coroutine Threading manager which starts the coroutine. So I think this is all useless for you. I thought this was Unity default stuff.
private IEnumerator FogOfWarProcess ()
{
while ( isRunning )
{
yield return null; //Not sure why that is for
if ( !IsVissible )
{
continue;
}
yield return UnityCoroutine.Now;
//Inside Unity Thread
CollectData ();
m_FogMap.Apply ();
m_FogOverlayMap.Apply ();
yield return ThreadCoroutine.Now;
//Inside Other Thread
CalculateFogOfWar ();
System.Threading.Thread.Sleep ( THREAD_SLEEP_TIME );
}
}
//Other stuff
public sealed class UnityCoroutine
{
public static readonly UnityCoroutine Now = new UnityCoroutine ();
private UnityCoroutine () { }
}
public sealed class ThreadCoroutine
{
public static readonly ThreadCoroutine Now = new ThreadCoroutine ();
private ThreadCoroutine () { }
}
My first guess would be that it is expensive to start a Task every time, wich would lead me to implementing it like @Doug_B suggested.
if i comment out the LateUpdate method my app runns smoothly. So something there is still pretty expensive for the main thread it seems.
The app runs on the HoloLens, maybe it has something to do with that?
About the main question of thread. Iâve few solutions where Iâm combining threads and coroutines. Usually my approach is to start a thread with logic and the coroutine is waiting until the thread is finished.
Thanks a lot.
I now have only one Task wich runs in the Background, instead of starting a new Task in Update.
The app is pretty smooth right now. I have just a tiny bit lag when i try to capture the current view via WebCamTexture.Play and WebCamTexture.GetPixels32(). But These methods can only run on the Main thread as far as i know. So nothing to do here, but i am quite happy with how it is now.
Iâve solution where Iâm processing image from other source as Texture. Iâm doing this in VR and there is performance most important.
My solution is using Microsoft.Drawing.Bitmap for reading pixel colors data and write it to buffer in background thread. Of course itâs possible to use another library for reading picture pixel colors, depends on platform.
Then standart MonoBehaviour Update() is reading this buffer and itâs using it with Texture SetPixel(). After the background thread is finished main thread call Texture Apply().
Works great and keep performance. Speed of loading picture depends on itâs size, but usually take only few seconds.
a few seconds is quite long in this case.
i have a few ms delay at the Moment (10 ms or less) .
U can Show me ur Code how it is done if u like to, i may try it, but right now i like how it is and Focus on other parts.
Kind Regards
I need to say, we are usually opening pictures of size few MB. Sometimes bigger, like 20MB. If you can open big picture as texture in 10ms Iâm more interest about your solution.
Sorry I canât share this code. Itâs company property.