c# - 如何从必须保留在主线程上的方法统一调用异步方法
问题描述
我有一个名为 的方法SendWithReplyAsync
,它用于TaskCompletionSource
在完成时发出信号并从服务器返回回复。
我正在尝试以一种还需要更改场景、更改变换等的方法从服务器获得 2 个回复,因此据我所知,它需要位于主线程上。
这个方法OnClick()
统一绑定到一个ui按钮的:
public async void RequestLogin()
{
var username = usernameField.text;
var password = passwordField.text;
var message = new LoginRequest() { Username = username, Password = password };
var reply = await client.SendWithReplyAsync<LoginResponse>(message);
if (reply.Result)
{
Debug.Log($"Login success!");
await worldManager.RequestSpawn();
}
else Debug.Log($"Login failed! {reply.Error}");
}
如您所见,有一个电话await WorldManager.RequestSpawn();
该方法如下所示:
public async Task RequestSpawn()
{
//Get the initial spawn zone and transform
var spawnReply = await client.SendWithReplyAsync<PlayerSpawnResponse>(new PlayerSpawnRequest());
//Load the correct zone
SceneManager.LoadScene("TestingGround");
//Move the player to the correct location
var state = spawnReply.initialState;
player.transform.position = state.Position.Value.ToVector3();
player.transform.rotation = Quaternion.Euler(0, state.Rotation.Value, 0);
//The last step is to get the visible entities at our position and create them before closing the loading screen
var statesReply = await client.SendWithReplyAsync<InitialEntityStatesReply>(new InitialEntityStatesRequest());
SpawnNewEntities(statesReply);
}
因此,当我单击按钮时,我可以看到(服务器端)所有消息(登录请求、生成请求和初始实体状态请求)都在发出。然而,没有任何事情发生在合一中。没有场景变化和(显然)没有位置或旋转更新。
我有一种感觉,当涉及到统一时,我对 async/await 不了解,而且我的RequestSpawn
方法没有在主线程上运行。
我尝试在所有方法上使用client.SendWithReplyAsync(...).Result
和删除async
关键字,但这只会导致死锁。我在 Stephen Cleary 的博客上阅读了更多关于死锁的信息(似乎他的网站消耗了 100% 的 cpu ......我是唯一的一个吗?)
我真的不知道如何让这个工作。
如果您需要它们,以下是发送/接收消息的方法:
public async Task<TReply> SendWithReplyAsync<TReply>(Message message) where TReply : Message
{
var task = msgService.RegisterReplyHandler(message);
Send(message);
return (TReply)await task;
}
public Task<Message> RegisterReplyHandler(Message message, int timeout = MAX_REPLY_WAIT_MS)
{
var replyToken = Guid.NewGuid();
var completionSource = new TaskCompletionSource<Message>();
var tokenSource = new CancellationTokenSource();
tokenSource.CancelAfter(timeout);
//TODO Make sure there is no leakage with the call to Token.Register()
tokenSource.Token.Register(() =>
{
completionSource.TrySetCanceled();
if (replyTasks.ContainsKey(replyToken))
replyTasks.Remove(replyToken);
},
false);
replyTasks.Add(replyToken, completionSource);
message.ReplyToken = replyToken;
return completionSource.Task;
}
这是完成任务的位置/方式:
private void HandleMessage<TMessage>(TMessage message, object sender = null) where TMessage : Message
{
//Check if the message is in reply to a previously sent one.
//If it is, we can complete the reply task with the result
if (message.ReplyToken.HasValue &&
replyTasks.TryGetValue(message.ReplyToken.Value, out TaskCompletionSource<Message> tcs) &&
!tcs.Task.IsCanceled)
{
tcs.SetResult(message);
return;
}
//The message is not a reply, so we can invoke the associated handlers as usual
var messageType = message.GetType();
if (messageHandlers.TryGetValue(messageType, out List<Delegate> handlers))
{
foreach (var handler in handlers)
{
//If we have don't have a specific message type, we have to invoke the handler dynamically
//If we do have a specific type, we can invoke the handler much faster with .Invoke()
if (typeof(TMessage) == typeof(Message))
handler.DynamicInvoke(sender, message);
else
((Action<object, TMessage>)handler).Invoke(sender, message);
}
}
else
{
Debug.LogError(string.Format("No handler found for message of type {0}", messageType.FullName));
throw new NoHandlersException();
}
}
手指交叉传奇的斯蒂芬克利里看到了这个
解决方案
我建议您使用 UniRx.Async 库中的 UniTask,它为您提供了开箱即用的功能:
推荐阅读
- android - 我应该尽可能使用 Recyclerview 吗?
- php - 除了在 wordpress 中的日期之外,如何显示博客发布时间?
- python - 将 pandas 中的正则表达式值转换为 0 或 1
- javascript - Node - hackathon starter 无法加载静态文件?
- javascript - 无法在 document.write 元素之前设置属性“onclick”为 null
- memory-leaks - 每次碰撞迭代libGDX的渐进滞后
- android - 使用 Firestore 按键入的字母搜索
- google-cloud-platform - Google Cloud - 从不同的电子邮件地址使用了错误的项目 ID
- paypal - IPN 模拟器说接受,但验证总是返回“无效”
- c# - 在使用内存流作为 TiffBitmap 解码器的输入时需要帮助