Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 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
8
Question by jzhang1 · Aug 22, 2012 at 12:15 AM · thread

How do I invoke functions on the main thread?

Is there anyway to invoke unity functions on the main thread? I'm building an Editor script that runs workers in background threads due to large processing loads so as not to freeze up the editor. I need to be able to call unity functions on the final result but this needs to run in the main thread. Is there a way to do this?

Comment
Add comment · Show 1
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 Fattie · Jan 14, 2019 at 03:51 PM 0
Share

Before doing any of that you should read this:

https://stackoverflow.com/a/54184457/294884

threading + frame based system causes vast confusion

7 Replies

· Add your reply
  • Sort: 
avatar image
10
Best Answer

Answer by MadDave · Aug 22, 2012 at 10:16 AM

You cannot really call into a running thread; instead, the thread you want to call (the main thread in your case) needs to maintain a queue of events. Other threads can push events into the queue and the owning thread will consume them from time to time. This is called the producer-consumer-pattern. An 'event' in this context is just a data structure that contains whatever information you need to send to the other thread.

That implies, of course, that the main thread will not receive the call immediately (synchronously) but with a delay. Well, that is the nature of threading. You cannot 'interrupt' a thread to make a call into it. If you need to pass data in both directions both threads will need a queue.

The queue will need to be thread-safe, of course. Simply lock it whenever you access it.

.NET has a thread dispatcher class that implements that pattern in a nice way and it even feels like making function calls. I am not sure you can use it in Unity, though, since every thread may only have one dispatcher and the Unity main thread may already have one. May be worth trying, though.

http://msdn.microsoft.com/de-de/library/system.windows.threading.dispatcher%28v=vs.85%29.aspx

If it does not work you will have to implement your own queue solution.

Comment
Add comment · Show 6 · 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 hoffmanuel · Apr 23, 2013 at 01:04 PM 0
Share

Could you post a solution to this, as the question is marked as solved, please?

avatar image whydoidoit · Apr 23, 2013 at 01:08 PM 2
Share

Added a new answer with my solution.

avatar image cowlinator · Feb 27, 2015 at 08:30 PM 0
Share

The .NET thread dispatcher class is .NET 3.0+ only

avatar image Eric5h5 · Feb 27, 2015 at 08:40 PM 0
Share

Unity currently uses $$anonymous$$ono 2.6, which is approximately .NET 3.5. I don't know if it has System.Windows.Threading though.

avatar image pdwitte · Apr 02, 2016 at 12:53 AM 0
Share

I made a quick library that describes exactly what you're talking about. https://github.com/PimDeWitte/Unity$$anonymous$$ainThreadDispatcher

Show more comments
avatar image
21

Answer by pdwitte · Apr 02, 2016 at 05:24 AM

Hey guys,

I created a simple class for this reason that you can call with a one-liner.

You can use it like this:

 public IEnumerator ThisWillBeExecutedOnTheMainThread() {
     Debug.Log ("This is executed from the main thread");
     yield return null;
 }
 
 public void ExampleMainThreadCall() {
     UnityMainThreadDispatcher.Instance().Enqueue(ThisWillBeExecutedOnTheMainThread());
 }

Simply head over to https://github.com/PimDeWitte/UnityMainThreadDispatcher and start using it if you'd like.

Comment
Add comment · Show 1 · 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 gaberutd · Oct 05, 2016 at 08:51 PM 0
Share

Thank you for this! Just wondering; does the function queued always have to be an IEnumerator, or can we alter that aspect?

avatar image
10

Answer by Curious-George · Oct 07, 2017 at 08:45 PM


Uncomplicated and efficient:

 using System.Collections.Generic;
 using System.Threading;
 using System;
 using UnityEngine;
 
 public class Dispatcher : MonoBehaviour
 {
     public static void RunAsync(Action action) {
         ThreadPool.QueueUserWorkItem(o => action());
     }
 
     public static void RunAsync(Action<object> action, object state) {
         ThreadPool.QueueUserWorkItem(o => action(o), state);
     }
 
     public static void RunOnMainThread(Action action)
     {
         lock(_backlog) {
             _backlog.Add(action);
             _queued = true;
         }
     }
 
     [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
     private static void Initialize()
     {
         if(_instance == null) {
             _instance = new GameObject("Dispatcher").AddComponent<Dispatcher>();
             DontDestroyOnLoad(_instance.gameObject);
         }
     }
 
     private void Update()
     {
         if(_queued)
         {
             lock(_backlog) {
                 var tmp = _actions;
                 _actions = _backlog;
                 _backlog = tmp;
                 _queued = false;
             }
 
             foreach(var action in _actions)
                 action();
 
             _actions.Clear();
         }
     }
 
     static Dispatcher _instance;
     static volatile bool _queued = false;
     static List<Action> _backlog = new List<Action>(8);
     static List<Action> _actions = new List<Action>(8);
 }

Comment
Add comment · Show 1 · 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
6

Answer by whydoidoit · Apr 23, 2013 at 01:07 PM

You can easily create a dispatcher to talk to Unity's main loop by having a component read from a list of available actions. This Unity Gems Threading Article contains a package that can be used to achieve this.

It is used inconjunction with threads like this:

 //Scale a mesh on a second thread
 void ScaleMesh(Mesh mesh, float scale)
 {
     //Get the vertices of a mesh
     var vertices = mesh.vertices;
     //Run the action on a new thread
     Loom.RunAsync(()=>{
         //Loop through the vertices
         for(var i = 0; i < vertices.Length; i++)
         {
             //Scale the vertex
             vertices[i] = vertices[i] * scale;
         }
         //Run some code on the main thread
         //to update the mesh
         Loom.QueueOnMainThread(()=>{
             //Set the vertices
             mesh.vertices = vertices;
             //Recalculate the bounds
             mesh.RecalculateBounds();
         });
  
     });
 }

The download for the package is in the article. Basically using QueueOnMainThread passes the closure to be run on the main thread as soon as possible or after a specified delay.

Comment
Add comment · Show 5 · 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 hoffmanuel · Apr 24, 2013 at 09:40 AM 0
Share

Ok this is easy to get and works if you start perfor$$anonymous$$g your operations from the main thread itself. (as i understood so far) But if you have a function in your class, that gets called by an other thread via e.g. .net Remoting, this method will fail...*

 public void NotifyFinished(int status)
     {
         Debug.Log("Notify");
         try
         {
             if (status == (int)LevelStatusCode.O$$anonymous$$)
             {   
                 Loom.QueueOn$$anonymous$$ainThread(() =>
                 {
                     PresentNameInputController();
                 });   
             }
         }
         catch (Exception e)
         {
             Debug.LogError(e.ToString());
         }
     }

Do you possibly know how to solve that problem?

get_isPlaying can only be called from the main thread.

Constructors and field initializers will be executed from the loading thread when loading a scene. Don't use this function in the constructor or field initializers, ins$$anonymous$$d move initialization code to the Awake or Start function. I get that ArgumentException in Loom:Initialize()
avatar image Bunny83 · Apr 24, 2013 at 10:32 AM 3
Share

You have to initialize the Loom object first from the Unity thread. So just add this line to a Start method which runs before your thread:

 var tmp = Loom.Current;

This line will invoke the Initialize() function of Loom. Note this kine will preduce a warning on the tmp variable. The problem is that the Initialize method is private and all you can do is accessing the Current property ;)

Alternatives are:

  • edit the Loom script and make the Initialize method public, so you can call it from Start

  • Do something useless with the Loom-object.

That's a line which doesn't produce a warning:

 Loom.Current.GetComponent<Loom>();
avatar image hoffmanuel · Apr 24, 2013 at 10:45 AM 0
Share

THAN$$anonymous$$S, that helped!

It is working with

 void Start()
 {
     var tmp = Loom.Current;
     ...
 }
 //Function called from other Thread
 public void NotifyFinished(int status)
 {
     Debug.Log("Notify");
     try
     {
         if (status == (int)LevelStatusCode.O$$anonymous$$)
         {
             Loom.QueueOn$$anonymous$$ainThread(() =>
             {
                 PresentNameInputController();
             });
         }
     }
     catch (Exception e)
     {
         Debug.LogError(e.ToString());
     }
 }

Great work with this!!

avatar image whydoidoit · Apr 24, 2013 at 10:47 AM 2
Share

Just a note - if you aren't using the closure you can just do:

   Loom.QueueOn$$anonymous$$ainThread(PresentNameInputController);
avatar image leegod · Nov 18, 2015 at 04:01 AM 0
Share

Hi. I found same problem. I need to call function which is in another thread.

What is Loom? how to make it work?

Your link ( Unity Gems Threading Article) does not work anymore.

Please let me know.

avatar image
2

Answer by PAEvenson · Aug 29, 2013 at 02:22 PM

If anyone is interested I reworked the Loom Class from @whydoidoit 's Unity Gems link to an EditorWindow. This way you can easily thread long processes without locking up the editor window.

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 using UnityEditor;
 using System.Threading;
 using System;
 using System.IO;
 using System.Linq;
 
 public class LoomEditorWindow : EditorWindow {
     
     public int maxThreads = 8;
     
     private int numThreads;
     private int _count;
     
     private bool m_HasLoaded = false;
     
     private List<Action> _actions = new List<Action>();
     private List<DelayedQueueItem> _delayed = new  List<DelayedQueueItem>();
 
     private List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
     private List<Action> _currentActions = new List<Action>();
     
     public struct DelayedQueueItem
     {
         public float time;
         public Action action;
     }
     
     protected void QueueOnMainThread(Action action)
     {
         QueueOnMainThread( action, 0f);
     }
     
     protected void QueueOnMainThread(Action action, float time)
     {
         if(time != 0)
         {
             lock(_delayed)
             {
                 _delayed.Add(new DelayedQueueItem { time = Time.time + time, action = action});
             }
         }
         else
         {
             lock (_actions)
             {
                 _actions.Add(action);
             }
         }
     }
     
     protected Thread RunAsync(Action a)
     {
         while(numThreads >= maxThreads)
         {
             Thread.Sleep(1);
         }
         Interlocked.Increment(ref numThreads);
         ThreadPool.QueueUserWorkItem(RunAction, a);
         return null;
     }
     
     private void RunAction(object action)
     {
         try
         {
             ((Action)action)();
         }
         catch
         {
         }
         finally
         {
             Interlocked.Decrement(ref numThreads);
         }
     }
     
     protected virtual void Start()
     {
         m_HasLoaded = true;
         
     }
     
     protected virtual void Update()
     {
         if(m_HasLoaded == false)
             Start();
         
         lock (_actions)
         {
             _currentActions.Clear();
             _currentActions.AddRange(_actions);
             _actions.Clear();
         }
         foreach(var a in _currentActions)
         {
             a();
         }
         lock(_delayed)
         {
             _currentDelayed.Clear();
             _currentDelayed.AddRange(_delayed.Where(d=>d.time <= Time.time));
             foreach(var item in _currentDelayed)
                 _delayed.Remove(item);
         }
         foreach(var delayed in _currentDelayed)
         {
             delayed.action();
         }
     }
 }
 

 
Comment
Add comment · 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
  • 1
  • 2
  • ›

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

25 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

ThreadCheck error in socket programming and instantiate a object!!! 1 Answer

How do I close the 'System.Threading.Timer' in my UnityEditor 1 Answer

How to change UI or GameObject with Thread 0 Answers

How does unity thread works? 0 Answers

Threads and variable lock 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