Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Async Await System.Threading.Task and Unity Main Thread

Discussion in 'Scripting' started by OnoSaburo, Aug 15, 2023.

  1. OnoSaburo

    OnoSaburo

    Joined:
    Mar 25, 2023
    Posts:
    18
    Hi
    I was trying to make a ServiceManager which can do some utility work.
    The ServiceManager can send a verification code email to a user, can access to database and can transact simple ETH transfer transaction.
    I decided to make this ServiceManager class as pure C# class.
    I mean the ServiceManager was not derived from MonoBehaviour.
    All the funtions were static and returned async Task<...>.
    Here is a part of the ServiceManager class source code.
    upload_2023-8-15_16-23-20.png

    upload_2023-8-15_16-23-46.png

    All things worked very well exept for one thing.
    RequestManager is derived from MonoBehaviour
    Is this a problem of relation between System.Threading.Task and Unity main Thread?
    How can I overcome this obstacle?

    Code (CSharp):
    1. Top level scope allocation is permitted only on the main thread.
    2. UnityEngine.StackTraceUtility:ExtractStackTrace ()
    3. Unity.Netcode.FastBufferWriter:.ctor (int,Unity.Collections.Allocator,int) (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.5.2/Runtime/Serialization/FastBufferWriter.cs:99)
    4. Unity.Netcode.NetworkBehaviour:__beginSendClientRpc (uint,Unity.Netcode.ClientRpcParams,Unity.Netcode.RpcDelivery) (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.5.2/Runtime/Core/NetworkBehaviour.cs:117)
    5. RequestManager:Response_Deposit_ClientRpc (TransactionDepositResult,System.Decimal,Unity.Netcode.ClientRpcParams) (at Assets/Scripts/Server/RequestManager.cs:202)
    6. RequestManager:DepositDetected (TransactionDepositResult,System.Decimal,ulong) (at Assets/Scripts/Server/RequestManager.cs:196)
    7. ServiceManager/<Deposit>d__11:MoveNext () (at Assets/Scripts/Server/ServiceManager.cs:504)
    8. System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<string>:SetResult (string)
    9. ServiceManager/<SimpleEtherTransferAsync>d__26:MoveNext () (at Assets/Scripts/Server/ServiceManager.cs:687)
    10. System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<Nethereum.RPC.Eth.DTOs.TransactionReceipt>:SetResult (Nethereum.RPC.Eth.DTOs.TransactionReceipt)
    11. Nethereum.RPC.TransactionReceipts.TransactionReceiptPollingService/<SendRequestAndWaitForReceiptAsync>d__8:MoveNext ()
    12. System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<Nethereum.RPC.Eth.DTOs.TransactionReceipt>:SetResult (Nethereum.RPC.Eth.DTOs.TransactionReceipt)
    13. Nethereum.RPC.TransactionReceipts.TransactionReceiptPollingService/<PollForReceiptAsync>d__9:MoveNext ()
    14. System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<Nethereum.RPC.Eth.DTOs.TransactionReceipt>:SetResult (Nethereum.RPC.Eth.DTOs.TransactionReceipt)
    15. Nethereum.JsonRpc.Client.ClientBase/<SendRequestAsync>d__8`1<Nethereum.RPC.Eth.DTOs.TransactionReceipt>:MoveNext ()
    16. System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<Nethereum.RPC.Eth.DTOs.TransactionReceipt>:SetResult (Nethereum.RPC.Eth.DTOs.TransactionReceipt)
    17. Nethereum.JsonRpc.Client.ClientBase/<SendInnerRequestAsync>d__12`1<Nethereum.RPC.Eth.DTOs.TransactionReceipt>:MoveNext ()
    18. System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<Nethereum.JsonRpc.Client.RpcMessages.RpcResponseMessage>:SetResult (Nethereum.JsonRpc.Client.RpcMessages.RpcResponseMessage)
    19. Nethereum.JsonRpc.Client.RpcClient/<SendAsync>d__20:MoveNext ()
    20. System.Threading.Tasks.TaskFactory`1<System.Net.WebResponse>:FromAsyncCoreLogic (System.IAsyncResult,System.Func`2<System.IAsyncResult, System.Net.WebResponse>,System.Action`1<System.IAsyncResult>,System.Threading.Tasks.Task
    21.  
    upload_2023-8-15_16-21-12.png
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    3,899
    Please post code as text and put it in code tags. Screenshots are not quotable and often hard to read.

    If your servicemanager raises an event from another thread then the receiver must not do anything Unity API related (such as GetComponent). There are only very few exceptions. The most basic thing to do is when your monobehaviour executes a method called by your non-mainthread servicemanager then you should set a bool to true and check this in Update so that you run the event code on the main thread.

    It may be possible to simply start a coroutine instead of the bool but even StartCoroutine may not be allowed to be called from anoher thread.
     
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
  4. OnoSaburo

    OnoSaburo

    Joined:
    Mar 25, 2023
    Posts:
    18
    Thank you very much for your advice?
    But I have a serious question.
    Do you really do this when you are in my shoe?
    ServiceManager says "I updated sth, So I set the flag as true. Hey, Update() of MonoBehaviour. Please check it and do sth for it".
    What if I have 256 players and they have their own wallet balance to check (so 256 wallets)?
    256 notification flags?
    When I have another values to check?
    256 * n notification flags?

    I think that will make the code somewhat messy and unmaintainable.
    Sorry. I don't want to hurt you. Just my opinion.

    I do not want to use Third Party solutions.
    I want to resolve this within Unity and System.Treading.Task
    I will try to resolve this and let you know the result.
    If you have any good thought please share here kindly.
     
  5. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    It's an inherent limitation of Unity. You can't do 99% of Unity things while off of the main thread.

    You might find a solution using tools like UniTask. I know you say you don't want third party solutions, but UniTask is pretty battle tested and allows better async support in Unity land.
     
  6. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,256
    Why would 256 people be using the same client?

    It will only be one client at any given time.

    There may be 256 requests to the server, that would mean 256 threads but on 256 different computers or instances of Unity with only one flag to check each.
     
  7. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563