调用 Task.Run(( 之外的方法。我有这个代码
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])));
}
});
}
代码等待广播消息,然后将字符串发送到 AddUniqueRoomList。我想要的只是向AddUniqueRoomList方法发送一条消息,而不将其包含在Task.Run中。AddUniqueRoomList 方法创建一个 UI 按钮,如果它位于 task.run(( 中,则会导致错误,因为 Unity 在多线程方面表现不佳。
与注释中一样,您可以使用通常称为"调度程序"的"调度程序",以便让任何Action
在 Unity 主线程中执行。
最简单的方法实际上是直接使用线程保存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();
}
}
}
将此脚本放在场景中的任何活动对象上,以便一直调用其更新。
然后在脚本中,您不会直接执行该方法,而是通过
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);
}
);
}
});
}