【发布时间】:2019-05-05 06:28:29
【问题描述】:
我有一个代码 sn-p,它的作用类似于 Unity 中的 Grpc 客户端。代码风格是为控制台应用程序设计的,可以在Main方法中调用,阻塞它并一直接收数据。现在,我想在 Unity 中使用它,显然我希望我的应用程序同时在 Unity 中运行。此外,我的最终目标是拥有像 Udp 客户端一样工作的东西。您只需调用一次,它就会一直为您接收数据,而不会阻塞宿主应用程序的任何部分。
这个设计最重要的部分是,如果有任何事件,我会得到更新,如果没有新事件,相应地,我不会收到任何数据。它发生在 ObserveEvents(channel).Wait(); 中。问题是 Wait();。这一直是,保持主线程,工作线程,听更新。在播放模式下,Unity 不再响应!
我可以绕过它说,我不需要这样的设计,我可以每隔一秒或每隔几帧接收一次事件。通过这样做,我拥有了我的 Unity 应用程序,但我失去了很多帧速率,不管我的数据没有顺利地流向我在 Unity 中的主机应用程序。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Grpc.Core;
using UnityEngine;
namespace Scripts
{
public class GrpcChannel
{
public void GrpcServer(string ip, int port)
{
var channel = new Channel(ip, port, ChannelCredentials.Insecure);
ObserveEvents(channel).Wait();
channel.ShutdownAsync().Wait();
}
private async Task ObserveEvents(Channel channel)
{
Debug.Log("Observing events...");
var eventService = new EventService.EventServiceClient(channel);
var request = new RegisterOnEvent();
using (var call = eventService.OnEvent(request))
{
var responseStream = call.ResponseStream;
while (await responseStream.MoveNext())
{
//var receivedEvent = responseStream.Current;
// on change of any data, this method will be fired
GetJointPosition(channel, "Flower_id_22134");
}
}
}
private void GetJointPosition(Channel channel, string flowerName)
{
var client = new JointPositionService.JointPositionServiceClient(channel);
var request = new GetJointPositionRequest
{
FlowerName = flowerName
};
var response = client.GetJointPositionAsync(request);
SavePositions(response.ResponseAsync.Result.Positions);
}
private void SavePositions(IEnumerable<JointPosition> positions)
{
var jointPositions = positions.ToList();
for (var i = 0; i < Instance.Ref.AxesValues.Length; i++)
{
var axeValueDegree = jointPositions[i].Value * 180 / Math.PI;
Instance.Ref.AxesValues[i] = (float)axeValueDegree;
}
}
}
}
我这样称呼它:
var grpcChannel = new GrpcChannel();
grpcChannel.GrpcServer("192.168.123.16", 30201);
在 Update() 方法中。不幸的是,它在 Start() 方法中不起作用。是的,显然,它需要创建一个新 Channel 的每一帧,否则它将无法工作。
目前的实现是这样的,每 7 帧调用一次,没有使用特殊的等待事件设计:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using TMPro;
using UnityEngine;
namespace Assets.Scripts
{
public class AxisValuesService : MonoBehaviour
{
public TextMeshProUGUI[] AxesValuesTexts;
[HideInInspector] public Dictionary<uint, float> AxisValues = new Dictionary<uint, float>();
[HideInInspector] private int counter = 0;
private void Update()
{
counter++;
if (counter == 7)
{
try
{
var channel = new Channel("192.168.123.16", 30201, ChannelCredentials.Insecure);
GetJointPosition(channel, "Flower_id_22134");
//ObserveEvents(channel).Wait();
channel.ShutdownAsync().Wait();
}
catch (RpcException e)
{
Debug.LogError("Connection Error: " + e);
}
counter = 0;
}
}
private void GetJointPosition(Channel channel, string robotName)
{
// Request Axis Values
var client = new JointPositionService.JointPositionServiceClient(channel);
var request = new GetJointPositionRequest { RobotName = robotName };
var response = client.GetJointPositionAsync(request);
// Fill Dictionary
foreach (var i in response.ResponseAsync.Result.Positions)
{
double value = toDegree((double)i.Value);
AxisValues[i.Index] = (float)Math.Round(value, 2);
}
try
{
AxesValuesTexts[0].text = AxisValues[1].ToString();
AxesValuesTexts[1].text = AxisValues[2].ToString();
AxesValuesTexts[2].text = AxisValues[3].ToString();
AxesValuesTexts[3].text = AxisValues[4].ToString();
AxesValuesTexts[4].text = AxisValues[5].ToString();
AxesValuesTexts[5].text = AxisValues[6].ToString();
}
catch (Exception e)
{
Debug.Log("Dictionary problem.");
}
}
private double toDegree(double rad)
{
return (float)(180 / Math.PI) * rad;
}
}
}
我的问题是,首先,如果这个方法是完全异步,为什么它仍然在 Unity 中阻塞应用程序,以及我如何重新设计它以实现某些目标喜欢 Udp 或 Tcp 样式?
【问题讨论】:
-
ObserveEvents可能是async,但是您在返回的Task上调用Wait。Wait将阻塞直到Task完成。 -
@Ilian 感谢您的评论。如果我不调用 Wait,那么我的 ObserveEvents(channel);方法只会被调用一次。我会尝试寻找替代方案并回信给您。
-
在我看来,您根本没有使用线程,所有通信都可能发生在一个线程中,该线程只是将新数据汇集到更新可以检查每一帧的队列中
-
await ObserveEvents()和ObserveEvents().Wait()之间存在很大差异。第一个是异步的,另一个实际上阻塞了等待它完成的线程。我敢打赌,在 UI 线程上阻塞是应该避免的。我建议阅读 async/await 最佳实践。 msdn.microsoft.com/en-us/magazine/jj991977.aspx 和 docs.microsoft.com/en-us/windows/uwp/debug-test-perf/…
标签: c# multithreading unity3d asynchronous grpc