Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
1
Question by yss · Aug 26, 2016 at 01:18 PM · freezethreadsthreadingmultithreadingfreezing

Multithreading freezes editor

I outsourced my terrain generation into threads. The threads don't use any unity api stuff and all variables which the thread and the main thread have access to are thread safed via a lock.

My problem is that after a few seconds (sometime 3-4s and sometimes 30s or 40s) the unity editor freezes. If I open up the task manager the cpu and memory percentages are still changing (cpu is about 40-50% and memory about 300mb).

I've already tried unmanged threads and managed thread with the C# ThreadPool. The communication with the main thread goes over a status variable (which is locked) so if the task has finished the main thread collects the data and creates a mesh (every update the main thread checks for the status of the thread).

I've tried it on windows 10 with Unity 5.3/5.4 and on mac os x with unity 5.3/5.4.

Here's the shortened code:

Edit: Updated code, now using volatile

 public enum SurfaceCalculationStatus{
     NewJob,
     Processing,
     DataReady,
     Ready,
     Aborted
 }
 
 public class SurfaceCalculation {
 
     private SurfaceRenderType srt;
     
     //Some members...
 
     private volatile SurfaceCalculationStatus scs;
 
 
     public SurfaceCalculation(){
 
         scs = SurfaceCalculationStatus.Ready;
 
         ThreadPool.QueueUserWorkItem((o) => { ThreadFunction();});
     }
 
     public void Init(int x, int z, int size, int gridSize, SurfaceRenderType srt, Chunk c){
         //Some inits...
 
         scs = SurfaceCalculationStatus.NewJob;
         
     }
 
     private void ThreadFunction()
     {
         while (true) { 
             if(scs == SurfaceCalculationStatus.Aborted)
             {
                 return;
             }
             if (scs == SurfaceCalculationStatus.NewJob) {
                 scs = SurfaceCalculationStatus.Processing;
                 
                 //Heavy calculations    
                     
                 scs = SurfaceCalculationStatus.DataReady;
             }
 
             Thread.Sleep (100);
         }
     }
 
     public void AbortThread(){
         scs = SurfaceCalculationStatus.Aborted;
     }
 
     //Some Getter & Setters...
 
     public void SetReady(){
         scs = SurfaceCalculationStatus.Ready;
     }
 
     public bool IsReady(){
         return scs == SurfaceCalculationStatus.Ready;
     }
 
     public bool IsData(){
         return scs == SurfaceCalculationStatus.DataReady;
     }
 }
 
 

Edit: This is the class/logic which manages the threads. If I comment out the line

 sc.SetReady ();

at the Update() method there is no freezing.

 public class SurfaceFactory {
 
     private static List<Chunk>[] waitingQueue;
     private static SurfaceCalculation[] inProgressQueue;
 
     public static void Init(){
         waitingQueue = new List<Chunk>[WorldSettings.lvlRadii.Length];
         inProgressQueue = new SurfaceCalculation[SystemInfo.processorCount * 8];
 
         for (int i = 0; i < inProgressQueue.Length; i++) {
             inProgressQueue [i] = new SurfaceCalculation ();
         }
 
         for (int i = 0; i < waitingQueue.Length; i++) {
             waitingQueue [i] = new List<Chunk> ();
         }
     }
 
     public static void CreateSurface(Chunk c){
         if (GetFreeThreadCound() > 0) {
             StartSurfaceCalculation (c);
         } else {
             waitingQueue[c.GetLOD()].Add (c);
         }
     }
 
     public static void Update(){
         for (int i = inProgressQueue.Length - 1; i >= 0; i--) {
             SurfaceCalculation sc = inProgressQueue [i];
             if (sc.IsData ()) {
                 //Thread finished
                 /*Chunk c = sc.GetChunk();
                 Mesh mesh = new Mesh ();
                 mesh.vertices = sc.GetVertices ();
                 mesh.triangles = sc.GetIndices ();
                 mesh.RecalculateNormals ();
                 c.SetMesh (mesh);*/
                 sc.SetReady ();
             }
         }
 
         int numberOfWaitingChunks = 0;
 
         for (int i = 0; i < waitingQueue.Length; i++) {
             numberOfWaitingChunks += waitingQueue [i].Count;
         }
 
         int numberFreeThreads = GetFreeThreadCound ();
 
         while (numberOfWaitingChunks > 0 && numberFreeThreads > 0){
             for (int i = 0; i < waitingQueue.Length; i++) {
                 if (waitingQueue [i].Count > 0) {
                     Chunk c = waitingQueue [i][0];
                     waitingQueue[i].RemoveAt (0);
                     StartSurfaceCalculation (c);
                     numberFreeThreads--;
                 }
             }
         }
     }
 
     public static void AbortThreads(){
         for (int i = 0; i < inProgressQueue.Length; i++) {
             inProgressQueue [i].AbortThread ();
         }
     }
 
     private static void StartSurfaceCalculation(Chunk c){
         SurfaceCalculation sc = GetFreeThread ();
         if (sc != null) {
             sc.Init (c.GetChunkXMeters (false), c.GetChunkZMeters (false), c.GetSize (), c.GetGridSize (), c.GetSurfaceRenderType (), c);
         } else {
             waitingQueue [c.GetLOD ()].Add (c);
         }
     }
 
     private static int GetFreeThreadCound(){
         int c = 0;
         for (int i = 0; i < inProgressQueue.Length; i++) {
             if (inProgressQueue [i].IsReady ())
                 c++;
         }
         return c;
     }
 
     private static SurfaceCalculation GetFreeThread(){
         for (int i = 0; i < inProgressQueue.Length; i++) {
             if (inProgressQueue [i].IsReady ())
                 return inProgressQueue [i];
         }
         return null;
     }
 }
Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image
0

Answer by Bunny83 · Aug 26, 2016 at 07:35 PM

You never want to lock the whole code of the threaded function. That will lock every other thread that tries to aquire a lock while your function still runs. That completely destroys the purpose of using a thread in the first place and is prone for creating dead-locks.

How do you actually use that class? How do you managed those jobs? In such cases you usually don't need any locks as long as you declare your state variable as volatile. As long as the sequence when someone is allowed to change the state of the variable is clear there are no locks required.

Note: This only works for variable read / write operations which are "atomic". This is only given for primitive types (excluding double, long, ulong, decimal). Since the default underlying type for enums is "int", reading and writing an enum is an atomic operation. "volatile" ensures that the compiler does not do any optimisations on that variable.

So as long as only one thread will write the variable everything is fine. In your case the order is clear:

 state        Thread who has write-permission
 -----------------------------------------
 Ready        Main thread     --> can change to "NewJob" or "Aborted"
 NewJob       Worker thread   --> can change to "Processing" or "DataReady"
 Processing   Worker thread   --> can change to "DataReady"
 DataReady    Main thread     --> can change to "Ready" or "NewJob" or "Aborted"

However to be on the safe side it's better to declare seperate variables for commands and state. Commands are only set by the main thread and read by the worker and the state is only set by the worker and read by the main thread,

Anyways if you want to use a lock (maybe to allow setting the aborted state while still processing), make sure you hold the lock as short as possible.

  lock (locker)
  {
      scs = SurfaceCalculationStatus.Processing;
  }
  
  //Heavy calculations
  
  lock (locker)
  {
      if (scs ==SurfaceCalculationStatus.Aborted) // important to not overwrite an external abort request.
          return;
      scs = SurfaceCalculationStatus.DataReady;
  }

Also keep in mind that you always have to terminate the threads when running inside the editor. The playmode does not affect running threads. This can lead to massive problems inside the editor.

It's generally not recommended to use Thread.Abort, however in the case of Unity it might be a good idea as backup procedure.

It's generally adviced to have a dedicated "abort" variable which should be checked by the worker frequently during operation to terminate the thread within a short time.

Comment
Add comment · Show 2 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image yss · Aug 27, 2016 at 11:55 AM 0
Share

Thanks for your detailed answer. I changed my code so now I'm using volatile for the status which should work. I also edited my questions so now there's the updated code + the code of my manager class which creates the threads and manages them. You can also see when I quit the game the status of every thread will be changed which causes a return in the ThreadFunction so all threads will be shut down. Furthermore I recognized that if I comment out the line`sc.SetReady ();` in SurfaceFactory.Update() there is no freezing anymore. So something goes wrong when I reuse the thread to calculate some more data.

avatar image Bunny83 yss · Aug 27, 2016 at 04:38 PM 0
Share

You still seem to use the "scs" variable to ter$$anonymous$$ate the thread from outside, As i said that can be dangerous when you don't check if you can ter$$anonymous$$ate it at the moment. If the thread currently has the authority over the scs variable you shouldn't touch it from the outside.

Furthermore you shouldn't use a threadpool in your case. A threadpool is ment to manage the threads for you. You are supposed to queue work items that do process something and when the work is done the thread is returned to the pool. Since your thread function has an infinite loop the thread is never returned to the pool unless you ter$$anonymous$$ate your loop. In that case you should create the Thread yourself.

Also ins$$anonymous$$d of using "sleep" it's way better to use a AutoResetEvent. It allows a thread to wait for an event to occur. They are commonly used to let a thread sleep until it's required.

I have quickly written a general purpose JobQueue class. Where i implemented almost everything i've mentioned before. All you have to do is derive your own job class from "JobItem" and override the DoWork method. A Job should include all data that is needed to process the job and a place to store the result.

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

4 People are following this question.

avatar image avatar image avatar image avatar image

Related Questions

Best Practice for Multithreading Procedural Terrain Generation? 1 Answer

When I place a breakpoint in my code, it crashes Unity Editor 1 Answer

Getting current time from the worker thread 1 Answer

Multi threading problem ! 2 Answers

How do I call a function in a different CPU core? 2 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges