简体   繁体   English

在 Task.run 之外执行方法

[英]Execute method outside Task.run

Call a method outside of Task.Run().在 Task.Run() 之外调用一个方法。 I have this code我有这个代码

 private void StartListening(int numPort, int tasknumber)
    {
        udpClients[tasknumber] = new UdpClient();
        udpClients[tasknumber].Client.Bind(new IPEndPoint(IPAddress.Any, numPort));
        iPEndPoints[tasknumber] = new IPEndPoint(0, 0);
        Task.Run(() =>
        {
            while (true)
            {
                AddUniqueRoomList(iPEndPoints[tasknumber].Address.ToString(), Encoding.UTF8.GetString(udpClients[tasknumber].Receive(ref iPEndPoints[tasknumber])));
            }
        });

    }

The code waits for a broadcast message then send a string to AddUniqueRoomList.该代码等待广播消息,然后将字符串发送到 AddUniqueRoomList。 What I want is just send a message to AddUniqueRoomList method without including it in Task.Run.我想要的只是向 AddUniqueRoomList 方法发送一条消息,而不将其包含在 Task.Run 中。 AddUniqueRoomList method creates a UI button which causes an error if it's inside a task.run() cause unity doesn't do well with multithreading. AddUniqueRoomList 方法创建一个 UI 按钮,如果它在 task.run() 中,则会导致错误,因为 unity 不能很好地处理多线程。

As in the comments you can use an often referred to as "dispatcher" in order to let any Action be executed in the Unity main thread.在评论中,您可以使用通常称为“调度程序”的方法,以便在 Unity 主线程中执行任何Action

The simplest way is actually to directly use a thread-save ConcurrentQueue<Action> which doesn't require locks like最简单的方法实际上是直接使用不需要锁的线程保存ConcurrentQueue<Action>

public class MainThreadDispatcher : MonoBehaviour
{
    // Singleton pattern
    private static MainThreadDispatcher _instance;

    // This thread-save queue stores Actions to be executed in the main thread
    // any thread can add Actions to it via the ExecuteInUpdate method
    // then the existing actions will be executed in the Update loop
    // in the same order they came in
    private readonly ConcurrentQueue<Action> actions = new ConcurrentQueue<Action>();

    // This may be called by threads
    public static void ExecuteInUpdate(Action action)
    {
        if(!_instance)
        {
            Debug.LogError("You can talk but nobody is listening../nNo MainThreadDispatcher in scene!");
            return;
        }

        // Add the action as new entry at the end of the queue
        _instance.actions.Enqueue(action);
    }

    // Initialize the singleton
    private void Awake()
    {
        if(_instance && _instance != this)
        {
            // an instance already exists so destroy this one
            Destroy(gameObject);
            return;
        }

        _instance = this;
        DontDestroyOnLoad(this);
    }

    // This will be executed when the game is started
    // AFTER all Awake calls
    // See https://docs.unity3d.com/ScriptReference/RuntimeInitializeOnLoadMethodAttribute.html
    [RuntimeInitializeOnLoadMethod]
    private static void Initialize()
    {
        // If the instance exists already everything is fine
        if(_instance) return;

        // otherwise create it lazy
        // This adds a new GameObject to the scene with the MainThreadDispatcher
        // component attached -> this will call Awake automatically
        new GameObject("MainThreadDispatcher", typeof(MainThreadDispatcher));
    }

    // Executed in the Unity main thread
    private void Update()
    {
        // Every frame check if any actions are enqueued to be executed
        while(actions.TryDequeue(out car action)
        {
            action?.Invoke();
        }
    }
}

Put this script on any active object in the scene so it's Update is allways called.将此脚本放在场景中的任何活动对象上,以便始终调用它的 Update。

Then in your script you wouldn't execute the method directly but rather add it to this queue by doing然后在您的脚本中,您不会直接执行该方法,而是通过执行将其添加到此队列中

private void StartListening(int numPort, int tasknumber)
{
    udpClients[tasknumber] = new UdpClient();
    udpClients[tasknumber].Client.Bind(new IPEndPoint(IPAddress.Any, numPort));
    iPEndPoints[tasknumber] = new IPEndPoint(0, 0);
    Task.Run(() =>
    {
        while (true)
        {
            var receivedString = Encoding.UTF8.GetString(udpClients[tasknumber].Receive(ref iPEndPoints[tasknumber]));
            var address = iPEndPoints[tasknumber].Address.ToString();

            MainThreadDispatcher.ExexuteInUpdate(
                () => 
                { 
                    AddUniqueRoomList(address, receivedString);
                }
            );

        }
    });
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM