Search Unity

  1. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice
  2. Ever participated in one our Game Jams? Want pointers on your project? Our Evangelists will be available on Friday to give feedback. Come share your games with us!
    Dismiss Notice

[50USD] Easy threading - Multi threading made easy!

Discussion in 'Assets and Asset Store' started by arklay_corp, Aug 18, 2014.

  1. KnuckleCracker

    KnuckleCracker

    Joined:
    Dec 27, 2011
    Posts:
    63
    Yes, I understand the primary use cases of not blocking the main thread very well and I know how FixedUpdate is executed. The use case I described is also a perfectly valid threading use case. It is one of the reasons later versions of .net have this function: https://msdn.microsoft.com/en-us/library/system.threading.tasks.task.waitall(v=vs.110).aspx

    It's totally cool if your threading library doesn't encompass the use case I outlined. I'm not trying to make you go somewhere you don't want to go. I was just checking...

    I can of course do something like this:
    Code (csharp):
    1.  
    2.         ManualResetEvent resetEvent = new ManualResetEvent(false);
    3.         int threadCounter = thingsToDo.Count;
    4.         foreach(Thing s in thingsToDo) {
    5.              System.Threading.ThreadPool.QueueUserWorkItem((d) => {
    6.                 s.DoWork();
    7.                 if (Interlocked.Decrement(ref threadCounter) == 0) resetEvent.Set();
    8.             });
    9.         }
    10.         resetEvent.WaitOne();
    11.         resetEvent.Reset();
    12.  
    But, having consistency with other uses of threads via your Task based API makes for cleaner more maintainable code on my part. It's not a biggie, just a consistency thing. I'd rather your library be my only interface with threads if possible, hence my question if I had overlooked something.

    In summary, the use case I'm describing is where a batch of processing can be run in parallel, but the batch itself is part of a sequence that must be performed in order. 1 -> 2 -> 3 ->4 ->5 must happen each game frame. "3" is something that can be computed with parallel execution (like a compression, fractal, or other decoupled algorithm). 1 through 5 executes much faster in totality if you throw a bunch of threads at "3" such that it completes having been given more computing resources that run in parallel. But "4" needs the output of "3" before it can proceed. The entire sequence can't be deferred to a non-main thread and 1 through 5 must be done each physics update (or game update).
     
  2. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    If you want the main thread to be blocked and the tasks to be executed in order is easier than all of that:
    Code (CSharp):
    1. void FixedUpdate() {
    2.    //Do some stuff
    3.  
    4.    Expensive1();
    5.    Expensive2();
    6.    Expensive3();
    7. }
     
  3. KnuckleCracker

    KnuckleCracker

    Joined:
    Dec 27, 2011
    Posts:
    63
    Thanks for your time. You've answered my initial query.
     
  4. 2dgame

    2dgame

    Joined:
    Nov 24, 2014
    Posts:
    83
    Hello, arklay_corp
    I just purchased Easy Threading and have some trouble with the ContinueInMainThreadWith functionality.
    What I am trying to achieve is to pass a custom struct to a method to be run in the background. When it's finished I want it to pass another custom struct to another method to be run on the main thread. Here is how I set it up:
    Code (CSharp):
    1.  
    2. public class MultiThreader : MonoBehaviour {
    3.  
    4.     void Start ()
    5.     {
    6.         Task<StructA>.Run(() =>backgroundFunction(new StructA(10))).ContinueInMainThreadWith(mainThreadFunction);
    7.     }
    8.  
    9.     public StructB backgroundFunction(StructA structA)
    10.     {
    11.         return (new StructB("Hello world!"));
    12.     }
    13.  
    14.     public void mainThreadFunction(StructB structB)
    15.     {
    16.         Debug.Log(structB.b);
    17.     }
    18.  
    19.     public struct StructA
    20.     {
    21.         public int a;
    22.    
    23.         public StructA(int _a)
    24.         {
    25.             a = _a;
    26.         }
    27.     }
    28.  
    29.     public struct StructB
    30.     {
    31.         public string b;
    32.    
    33.         public StructB(string _b)
    34.         {
    35.             b = _b;
    36.         }
    37.     }
    38. }
    I do get an error for the line in the Start method:
    Can you help me to resolve this one?

    edit: Figured it out by looking into the example scripts. If anyone is curious, what I need is this:
    Code (CSharp):
    1. Task<StructB>.Run(() =>backgroundFunction(new StructA(10))).ContinueInMainThreadWith((r) =>mainThreadFunction(r.Result));
     
    Last edited: Feb 5, 2017
  5. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    Yeah, great solution, sorry I didn't got to help you faster
     
  6. thibautvdumont

    thibautvdumont

    Joined:
    Oct 6, 2015
    Posts:
    21
    Hello !

    First of all, thanks for your solution. After updating it, I find myself struggling with the new non-blocking Wait(). I know you intend to enforce the use of ContinueWith, however I can't manage to create the loop I need with it.

    I need a unique thread, runing a HeavyProcessing function and requesting data from the microphone in the main thread everytime it finishes. On most of recent phones it requests data almost every frame, which is why I would like to avoid creating a new thread.

    My previous (working) solution was roughly:
    Code (CSharp):
    1. _threadedTask = Task.Run(() => {
    2.      while (!_stopRecording)
    3.      {
    4.         Task<Record> t = Task<Record>.RunInMainThread(GetMicrophoneData);
    5.          t.Wait();
    6.          if(t.Result != null)
    7.               HeavyProcessing(t.Result);
    8.       }
    9. });
    10.  
    And this is my unsatisfying current code generating GC and starting a thread every frame:
    Code (CSharp):
    1.  void Loop(TaskCompletionSource<bool> tcs, U3D.Threading.ThreadPool pool)
    2. {
    3.     if (!_stopThreadOrder)
    4.     {
    5.         try
    6.         {
    7.              Record record = GetMicrophoneData();
    8.  
    9.              Task.Run(() =>
    10.              {
    11.                  if (!_stopThreadOrder && record != null )
    12.                      HeavyProcessing(record);
    13.                  Task.RunInMainThread(() => { Loop(tcs, pool); });
    14.              }, pool);
    15.         }
    16.         catch(Exception e)
    17.         {
    18.                     tcs.SetError(e);
    19.         }
    20.     }
    21.     else
    22.     {
    23.         _stopThreadOrder = false;
    24.         tcs.SetResult(true);
    25.          Debug.Log("[THREAD] Terminated thread properly");
    26.      }
    27. }
    I tried doing something like:
    Code (CSharp):
    1. Loop(Task t, Record record) {
    2.     t.ContinueWith((tt) => {
    3.         HeavyProcessing(record);
    4.         Task.RunInMainThread( (no_usage) => {
    5.             Record record = GetMicrophoneData();
    6.             Loop(t, record);
    7.         });
    8.    });
    9. }
    10.  
    11. Loop(Task.Run(() => {}), new Record());
    But the HeavyProcessing function get executed in the main thread with the last code.

    Could you give me a hand understanding how you intended this use-case to be solved ?

    Cheers !
     
    Last edited: Feb 6, 2017
  7. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    Hi @thibautvdumont ,

    Yes, you are right, I shouldn't have tried to force anyone to use continuation actions..., sorry about that.

    Now, to solve your issue you could implement something like this:
    Code (CSharp):
    1.        
    2.         System.Threading.ManualResetEvent mre =
    3.             new System.Threading.ManualResetEvent(false);
    4.         _threadedTask = Task.Run(() => {
    5.             while (!_stopRecording)
    6.             {
    7.                 Task<Record> t = Task<Record>.RunInMainThread(GetMicrophoneData);
    8.                 t.Wait((_) => mre.Set());
    9.                 mre.WaitOne();
    10.                 if (t.Result != null)
    11.                     HeavyProcessing(t.Result);
    12.             }
    13.         });
    14.  
     
    thibautvdumont likes this.
  8. thibautvdumont

    thibautvdumont

    Joined:
    Oct 6, 2015
    Posts:
    21
    @arklay_corp

    Thanks to your fast answer, it's working smoothly now. I just replaced the ManualResetEvent for an AutoResetEvent in regard of the expected behaviour.

    Best,
     
    arklay_corp likes this.
  9. arcdragon1

    arcdragon1

    Joined:
    Oct 15, 2012
    Posts:
    118
    Hi team. I try to make a loading bar. Is it possible to use combining this asset with SceneManager.LoadSceneAsync?
     
  10. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    hi, you can combine it, but i don't think is necesary... (you can just check the percentage in the Update method)
     
  11. arcdragon1

    arcdragon1

    Joined:
    Oct 15, 2012
    Posts:
    118
    Thanks arklay_corp.
    It was realized!
     
    arklay_corp likes this.
  12. NoblestMuffin

    NoblestMuffin

    Joined:
    Aug 24, 2012
    Posts:
    120
    Hey.

    I'm trying to launch some code on another thread in a Windows Store app. I use the following code:

    Code (CSharp):
    1. #if NETFX_CORE
    2. U3D.Threading.Tasks.Task.Run(() => ThreadSafeSlice.Slice(jobState));
    3. #else
    4. System.Threading.ThreadPool.QueueUserWorkItem (ThreadSafeSlice.Slice, jobState);
    5. #endif
    Nothing occurs. The code above is executed correctly but the thread function's body is never executed.

    I have no leads. Ask me anything.
     
  13. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    Try:
    Code (CSharp):
    1.  
    2. Debug.Log("Step 1");
    3. U3D.Threading.Tasks.Task.Run(() =>
    4. {
    5.     Debug.Log("Step 2");
    6.     ThreadSafeSlice.Slice(jobState);
    7. });
    That should work (at least the log entries)
     
  14. NoblestMuffin

    NoblestMuffin

    Joined:
    Aug 24, 2012
    Posts:
    120
    No, definitely not. Nothing ever happens. I'm digging into it with a debugger. I see it creates a thread with the task interfaces and tries to start it but nothing ever occurs. I'm not very familiar with this platform but I'm digging into it. Any other hints?
     
  15. NoblestMuffin

    NoblestMuffin

    Joined:
    Aug 24, 2012
    Posts:
    120
    I got it to work.

    It's written as though System.Threading.Tasks.Task starts automatically after the constructor. We have in ThreadPool.cs / Thread / Start for WSA, the method body is empty, commented out with "is already started". However in Thread / QueueAction, where the Task is created, it is never asked to start. The Task is created, transformed with a ContinueWith and bundled into your Thread class.

    I modified it like so:

    Code (CSharp):
    1. var task = new System.Threading.Tasks.Task(a);
    2. var task2 = task.ContinueWith((_th) => ThreadWillFinish((Thread)_th));
    3. task.Start();
    4. Thread th = (Thread)task2;
    And now it runs.

    I'm a little confused though about why it is written this way because I cannot find any claim in the documentation that Task starts automatically when created with the new operator and its constructor.

    But I imagine this must have passed testing.

    What's the story? What am I missing?
     
  16. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    Does the example works for you? Can you send me a project reproducing your problem?

    Maybe you have unity paused (it pauses when it loses focus and you don't have the run in background flag active)
     
  17. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    The asset is created to mimic (when possible) the Task class of .NET
     
  18. Studiomaurer

    Studiomaurer

    Joined:
    Sep 5, 2012
    Posts:
    36
    Hi, I'm trying to do an autocomplete function with multithreading, but am totally new to this topic.
    I cannot quite figure out the syntax, getting tons of errors just with these few lines

    Code (CSharp):
    1.  
    2. //keeping the task global so I can abort it
    3. private Task<List<string>> autoCompleteTask;
    4. private List<string> _IndexStrings;
    5.  
    6. public void autoCompleteIndex(string aSubString){
    7. //stop the task if a new request is triggered
    8.         if(!autoCompleteTask.IsAborted && !autoCompleteTask.IsCompleted)autoCompleteTask.AbortThread();
    9. //start new task
    10.         autoCompleteTask = Task<List<String>>.Run(autoComplete(_IndexStrings, aSubString));
    11.         autoCompleteTask.ContinueInMainThreadWith(autoCompleteDone);
    12.    }
    13.  
    14. public List<string> autoComplete(List<string> data, string aSubString){
    15. //do some autocomplete magic
    16. return new List<String>();
    17. }
    18.  
    19.     public void autoCompleteDone(Task<List<String>> t){
    20. //updating the data on the main thread
    21.         _masterControl._mainMenu.updateDataSource(t.result);
    22.     }
     
  19. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    I solved a couple of syntax mistakes on your code:

    Code (CSharp):
    1. //keeping the task global so I can abort it
    2.     private Task<List<string>> autoCompleteTask;
    3.     private List<string> _IndexStrings;
    4.  
    5.     public void autoCompleteIndex(string aSubString)
    6.     {
    7.         //stop the task if a new request is triggered
    8.         if (!autoCompleteTask.IsAborted && !autoCompleteTask.IsCompleted) autoCompleteTask.AbortThread();
    9.         //start new task
    10.         autoCompleteTask = Task<List<String>>.Run(() => autoComplete(_IndexStrings, aSubString));
    11.         autoCompleteTask.ContinueInMainThreadWith(autoCompleteDone);
    12.     }
    13.  
    14.     public List<string> autoComplete(List<string> data, string aSubString)
    15.     {
    16.         //do some autocomplete magic
    17.         return new List<String>();
    18.     }
    19.  
    20.     public void autoCompleteDone(Task<List<String>> t)
    21.     {
    22.         //updating the data on the main thread
    23.         _masterControl._mainMenu.updateDataSource(t.Result);
    24.     }
     
  20. Studiomaurer

    Studiomaurer

    Joined:
    Sep 5, 2012
    Posts:
    36
    Many thanks for the Quick answer! Missed that one...

    I can start the app now, but the task stops at this line in my atocomplete code:
    Code (CSharp):
    1. toBesearched = data.Where(
    2.                         p => compareInfo.IndexOf(p, substring, CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase) > -1);
    Can't I do this in multithreading? It was working before just fine...
     
  21. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    Without more context I cannot help you, that line looks good
     
  22. MardukCorp

    MardukCorp

    Joined:
    May 21, 2016
    Posts:
    76
    Is it somehow possible to do UnityWebRequest or something similar off the main thread?
    I'm getting the exception "InternalCreate can only be called from the main thread" when trying it. :/
     
  23. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    @MardukCorp some call you are invoking for a thread is trying to create a unity object, this needs to be done in the main thread
     
  24. Threepwood

    Threepwood

    Joined:
    Jul 23, 2009
    Posts:
    91
    @arklay_corp

    What's the proper way to start a thread, but pass in some data to the thread method?

    Say you want to start a thread called: DoSomeWork(Vector2 position) or DoSomeWork(15)?
     
  25. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    Code (CSharp):
    1. for (int i = 0; i<100; i++)
    2. {
    3.    StartThread(i);
    4. }
    5. ...
    6. void StartThread(int value)
    7. {
    8.    Task.Run(() => DoSomeWork(value));
    9. }
    Enjoy :)
     
  26. Threepwood

    Threepwood

    Joined:
    Jul 23, 2009
    Posts:
    91
    @arklay_corp
    Thanks! Another quick question...

    What's the easiest way to just have one thread that runs, over and over, each time it's done, but without allocating a new thread each time and destroying the old one? (reusing the old one)? I didn't see any thread pool examples in the docs or source. Assume a thread, or several threads that just build and light terrain thousands or 10s of thousands of times over their life.
     
  27. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    threadpools are implemented and you can use them, but for what you want to achieve, ContinueWith won't spawn another thread
     
  28. Threepwood

    Threepwood

    Joined:
    Jul 23, 2009
    Posts:
    91
    I don't really understand how to setup a thread and use ContinueWith, to loop back? Any chance getting a small example bit of code?

    I want to do a Task.Run() on a method (that will see if there's work to do then call a method that does all the work (slow).

    So if I do Task.Run (DoWork), how do I use ContinueWith to loop back and re-call DoWork again and again? I tried a few times and could not get it to work. I really just want the thread to instantiate, and forever loop and DoWork, and if it fails or stops I can just check and restart it.

    Anyway, thanks. Great asset overall. Just looking to optimize.
     
  29. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    One option would be:
    Code (CSharp):
    1.     void StartThread()
    2.     {
    3.         Task.Run(DoWork).ContinueWith(CheckError);
    4.     }
    5.     void DoWork()
    6.     {
    7.         while (true)
    8.         {
    9.             // Do some actual work
    10.         }
    11.     }
    12.     void CheckError(Task t)
    13.     {
    14.         Exception error = t.Exception;
    15.         // Check the exception and leave some log or something
    16.         StartThread();
    17.     }
    18.  
    But it might create a new thread (or reuse one from the pool). If you want to stay always in the same thread I would just do:
    Code (CSharp):
    1.     void StartThread()
    2.     {
    3.         Task.Run(DoWork);
    4.     }
    5.     void DoWork()
    6.     {
    7.         while (true)
    8.         {
    9.             try
    10.             {
    11.                 // Do some actual work
    12.             }
    13.             catch (Exception error)
    14.             {
    15.                 // Check the exception and leave some log or something
    16.             }
    17.         }
    18.     }
    19.  
    I hope this helped you
     
  30. Threepwood

    Threepwood

    Joined:
    Jul 23, 2009
    Posts:
    91
    Thanks @arklay_corp. A new question! :)

    Getting an exception and no call stack that is meaningful to me:

    Faulted task 2: System.NullReferenceException: Object reference not set to an instance of an object
    at U3D.Threading.Tasks.Task.CheckAbort () [0x00001] in /Users/georgeb/Unity Projects/CubeRPGGame/Assets/Plugins/Arklay/U3D/Threading/Tasks/Task.cs:143
    at U3D.Threading.Tasks.Task.<RunAsync>m__0 () [0x0000e] in /Users/georgeb/Unity Projects/CubeRPGGame/Assets/Plugins/Arklay/U3D/Threading/Tasks/Task.cs:243
    UnityEngine.Debug:Log(Object)
    ChunkLightingManager:CheckThread2Status(Task) (at Assets/General/Scripts/ChunkLightingManager.cs:69)
    U3D.Threading.Tasks.<ContinueWith>c__AnonStorey0:<>m__0(Task) (at Assets/Plugins/Arklay/U3D/Threading/Tasks/Task.cs:321)
    U3D.Threading.Tasks.Task:set_m_state(TState) (at Assets/Plugins/Arklay/U3D/Threading/Tasks/Task.cs:55)
    U3D.Threading.Tasks.Task:<RunAsync>m__0() (at Assets/Plugins/Arklay/U3D/Threading/Tasks/Task.cs:259)
    U3D.Threading.<QueueAction>c__AnonStorey0:<>m__0(Object) (at Assets/Plugins/Arklay/U3D/Threading/ThreadPool.cs:124)

    The only class of mine above, ChunkLightingManager, just points to the callback method for the thread when it's over, where the exception is generation. Here's the code for it (It does nothing):

    Code (CSharp):
    1.  
    2.     void CheckThread2Status(Task t)
    3.     {
    4.         // IsFaulted, IsCompleted, IsAborted (all bools on the task - set by states)
    5.         if (t.IsFaulted)
    6.         {
    7.             Debug.Log("Faulted task 2: " + t.Exception);
    8.         }
    9.         else if (t.IsAborted)
    10.         {
    11.             Debug.Log("Aborted task 2: " + t.Exception);
    12.         }
    13.         else if (t.IsCompleted)
    14.         {
    15.         }
    16.     }
    Any idea how I can track down that bad object reference? I've tried carpet bombing the code check that everything has a value as it should, but nothing. Any chance it's an error on your end? Help! :)

    EDIT: Hmmm upon closer look, appears that just accessing t.Exception above is what's throwing the error. I commented that out and it gave the message but no error about object reference? Any thoughts? Bug? Safe to ignore?

    Thank you.
     
    Last edited: Aug 29, 2017
  31. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    Are you building for WEBGL?
     
  32. Threepwood

    Threepwood

    Joined:
    Jul 23, 2009
    Posts:
    91
    No, it's a desktop build for Mac OSX. I get the same errors in the player.log as well.
     
  33. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    How/when are you calling CheckThread2Status?
     
  34. Threepwood

    Threepwood

    Joined:
    Jul 23, 2009
    Posts:
    91
    Whenever I kick off a lighting thread via this line:

    Code (CSharp):
    1. lightTask2 =  Task.Run(chunkRenderer.BuildAndLight).ContinueWith(CheckThread2Status);
    This seemed consistent with your examples.
     
  35. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    Can you pass me on private a project to reproduce the issue?
     
  36. Threepwood

    Threepwood

    Joined:
    Jul 23, 2009
    Posts:
    91
    I can't pass the full project and it's likely too complex to make a smaller test case (will see if I have time).

    Question:

    For a specific task, ie: Task task;

    What are the valid states of the task? Is it just task.IsFaulted, task.IsAborted and task.IsCompleted? Is testing IsCompleted a foolproof way to know if the task is done? Is there a better way? I may have an issue trying to juggle two tasks and firing them off when they are "done". One task works fine, but introducing the second is where the exception fault was happening. Ultimately I have a queue of work and just pull one of the queue and spawn a task, and do the same for a second task.
     
  37. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    The only way there is a NullReferenceException there (you can check debugging that part of code) is if you are invoking CheckThread2Status in other place else than in the continuation action or if you are reusing a Task (you cannot do that, you cannot launch a task once it's completed).

    IsCompleted when the task is finished (fail or not), if you use continuation actions the task will always be in the completed state.

    The best way of getting notified when a task is completed is using the ContinueWith method, if you wan to launch a task when the other completes, the easiest code is:

    Code (CSharp):
    1. Task.Run(FirstTask).ContinueWith((t) =>
    2. {
    3.    if(t.isFaulted)
    4.       Debug.LogException(t.Exception);
    5.    else
    6.       SecondTask();
    7. });
     
  38. crudeMe

    crudeMe

    Joined:
    Jul 8, 2015
    Posts:
    81
    @arklay_corp, hi!

    Im trying to find a solution to game stuttering when it tries to combine children runtime. I'm a bit confused as Unity API is not threadsafe and so on. So, the question is can I access mesh and stuff?
    Thanks!

    upd: revisiting forum thread seems to answer my question - everything has to be accessed in the main thread. tremendous pity :c
     
    Last edited: Oct 18, 2017
  39. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    Yeah, there is no workaround about that, that's you can send a task to the main thread...
     
  40. RiccardoAxed

    RiccardoAxed

    Joined:
    Aug 29, 2017
    Posts:
    67
    Hi, I'm very interested in a multithread solution that could allow me to download and create huge textures at runtime, avoiding the typical breaks/hiccups on the main thread, caused by the coroutine approach (class WWW).

    I learned that Unity API in themselves can only be called on the main thread, and not all the available multithread solutions provide API calls on secondary threads. Does your plugin allow that?

    So, just for example, could I use your plugin to rewrite the following code, so to have a threaded (and hiccup-free) texture loading?

    Code (CSharp):
    1. public Material m_material;
    2. public string m_url;
    3.  
    4. void Start()
    5. {
    6.    StartCoroutine(GetTexture(m_url, m_material));
    7. }
    8.  
    9. IEnumerator GetTexture(string url, Material mat)
    10. {
    11.     using (WWW www = new WWW(url))
    12.     {
    13.         yield return www;
    14.         www.LoadImageIntoTexture((Texture2D)mat.mainTexture);
    15.  
    16.         www.Dispose();
    17.     }
    18. }
     
  41. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    Yes, that's one of the reasons for this plugin to exists, you need to use some other method to get the texture (avoid WWW that needs to be executed in the main thread, and bring the execution back to the main thread once you get the texture.

    Something like this:
    Code (CSharp):
    1. void Start()
    2.     {
    3.         Task<Texture>.Run(GetTexture).ContinueInMainThreadWith(taskResult =>
    4.         {
    5.             if (taskResult.IsFaulted)
    6.             {
    7.                 Debug.LogException(taskResult.Exception, this);
    8.             }
    9.             else
    10.             {
    11.                 m_material.mainTexture = taskResult.Result;
    12.             }
    13.         });
    14.     }
    15.  
    16.     private Texture GetTexture()
    17.     {
    18.         byte[] textureData = null;
    19.         // get the texture byte array "somehow" (eg. using System.Net.Sockets)
    20.         Texture2D ret = new Texture2D(1, 1);
    21.         ret.LoadRawTextureData(textureData);
    22.         return ret;
    23.     }
     
  42. RiccardoAxed

    RiccardoAxed

    Joined:
    Aug 29, 2017
    Posts:
    67
    Thank you for your answer.

    Have to say that approach scares me because - i guess - i should download the raw byte[] texture data, but unfortunately i'm dealing with 8K textures (it's a 360 VR slideshow) so i should download something like a 200Mb raw data rather than a 5Mb jpg.

    Do you think it's possibile to "somehow" ( :)) download a lighter jpg first, then extract the raw data to feed my Texture2D, doing everything in a secondary thread as well?
     
  43. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    The byte array can be compressed (jpg or png) doesnt need to be an uncompressed image
     
  44. madbeage

    madbeage

    Joined:
    Jan 16, 2014
    Posts:
    10
    I just wanted to check that I can return things like lists or dictionaries from a task?

    i.e. instead of:

    Task<int>.Run (CalculateSomeIntegerInTheBackground);


    something like:

    Task<Dictionary<int, List<Club>>>.Run(UpdateClubsInBackground);
     
  45. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    Yeah T can be any type
     
  46. madbeage

    madbeage

    Joined:
    Jan 16, 2014
    Posts:
    10
    Thanks for the quick reply. I'm having an issue with this test case:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class TestClass  {
    6.  
    7.     public int testInt = 5;
    8.  
    9.     public TestClass(int value) {
    10.  
    11.         this.testInt = value;
    12.  
    13.     }
    14. }
    15.  

    Code (CSharp):
    1.  
    2. Task<List<TestClass>>.Run (() => {
    3.         ReturnList(50);
    4. })
    5. .ContinueInMainThreadWith(ContinueWithList);
    6.  
    7.  
    Code (CSharp):
    1. List<TestClass> ReturnList(int value) {
    2.  
    3.         List<TestClass> list = new List<TestClass> ();
    4.         list.Add (new TestClass(value));
    5.  
    6.         return list;
    7.  
    8. }
    9.  
    10. void ContinueWithList(Task<List<TestClass>> t) {
    11.  
    12.     Debug.Log ("List has " + t.Result.Count + " entries");
    13.  
    14. }
    Gives this error:

    error CS0123: A method or delegate `SavedGameManager.ContinueWithList(U3D.Threading.Tasks.Task<System.Collections.Generic.List<TestClass>>)' parameters do not match delegate `System.Action<U3D.Threading.Tasks.Task>(U3D.Threading.Tasks.Task)' parameters

    If I replace List<TestClass> with List<int> it works fine.
     
    Last edited: Mar 21, 2018
  47. madbeage

    madbeage

    Joined:
    Jan 16, 2014
    Posts:
    10
    Figured it out in the end with help from a post higher up...

    Code (CSharp):
    1. Task<List<TestClass>>.Run (() =>
    2.             ReturnList(50))
    3.             .ContinueInMainThreadWith((r) => {
    4.             ContinueWithList(r);
    5. });
     
  48. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    Yeah, using lambda expresions is the solution there :)
     
  49. piotrO

    piotrO

    Joined:
    Dec 16, 2009
    Posts:
    39
    Hi Arklay,

    I just bought the asset and have a few questions:

    1) The sample scene is giving me some errors when I hit "Dispatch to main thread" several times. This is on Mac / unity editor. Screenshot attached.

    2) Is it possible to pause a background thread and resume it when a results from a main thread task are ready? Something like this:

    Code (CSharp):
    1. Task t1 = Task.Run(()=>{
    2.  
    3. //do some stuff in the background thread...
    4. ...
    5.  
    6. //I need some data from the main thread so I run this
    7. Task tm1 = Task.RunInMainThread(()=>{});
    8.  
    9. //I need the data from tm1 so I'd like the bg thread to pause util tm1 is done
    10. tm1.PauseUntilComplete();
    11.  
    12. //when tm1 is done continue with tm1.Result
    13.  
    14. });
    Of course I know I can use ContinueWith, but it will force me to rewrite a lot of code I have (I was using Unity Thread Helper it allowed this kind of behaviour)

    Thanks!
     

    Attached Files:

  50. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    234
    hi,

    1) seems to be a race condition, keep in mind the examples are just something fast and dirty to show the features, is not rock solid.
    2) you can use .Wait, I would recommend using ContinueWith tho
    Code (CSharp):
    1. Task t1 = Task.Run(()=>{
    2. //do some stuff in the background thread...
    3. ...
    4. //I need some data from the main thread so I run this
    5. Task tm1 = Task.RunInMainThread(()=>{}).Wait();
    6. //when tm1 is done continue with tm1.Result
    7. });
    This code should work
     
unityunity