英文:
pass data by reference during invoke
问题
我可以使用 invoke(见下面的代码)将数据从不同的线程传递到C#的主线程中,但如何反向传递数据呢?尝试通过引用传递似乎不起作用。由于存在多个线程,使用全局变量也不起作用。
理想情况下,我希望不断更新 WS_Client 函数中的本地变量,然后每当在 mywebsocketserver 中收到请求时,它会触发处理操作,该操作将获取该不断更新的变量并将其传递到 mywebsocketserver 线程。然后,mywebsocketserver 将通过套接字将数据发送出去。
以下是代码示例:
using UnityEngine;
using WebSocketSharp;
using WebSocketSharp.Server;
using System;
public class OrientationEvent
{
public event Action<Vector3> OnOrientationUpdate;
}
public class WS_Client : MonoBehaviour
{
public static GameObject _cube;
private Action<Vector3> callback;
WebSocket ws;
[SerializeField] float speed = 50.0f;
private Vector3 _orientation;
// Start is called before the first frame update
void Start()
{
_cube = GameObject.Find("MyCube");
if (_cube == null)
{
Debug.Log("找不到 MyCube 初始状态");
}
WebSocketServer wssv = new WebSocketServer(8080);
Action<Vector3> callback = HandleOrientationUpdate;
MyWebSocketServer wsService = new MyWebSocketServer(callback);
wssv.AddWebSocketService<MyWebSocketServer>("/MyWebSocket", () => wsService);
wssv.Start();
}
public WS_Client(OrientationEvent orientationEvent)
{
callback = HandleOrientationUpdate;
}
private void Update()
{
_cube.transform.rotation = Quaternion.Euler(_orientation);
}
private void HandleOrientationUpdate(Vector3 orientation)
{
Debug.Log("尝试旋转 MyCube");
Debug.Log(orientation);
// 使用来自事件的数据更新立方体的方向
try
{
_orientation = orientation;
Debug.Log("转换方向");
}
catch (Exception ex)
{
Debug.LogError("发生错误: " + ex.Message);
}
}
}
public class MyWebSocketServer : WebSocketBehavior
{
private Action<Vector3> callback;
public MyWebSocketServer(Action<Vector3> callback)
{
this.callback = callback;
}
protected override void OnMessage(MessageEventArgs e)
{
string data = e.Data;
string[] components = data.Split(' ');
Debug.Log("拆分组件");
float x = float.Parse(components[0]);
float y = float.Parse(components[1]);
float z = float.Parse(components[2]);
Debug.Log("解析组件");
Vector3 orientation = new Vector3(x, y, z);
try
{
callback?.Invoke(orientation);
Debug.Log("调用操作");
}
catch (Exception ex)
{
Debug.LogError("发生错误: " + ex.Message);
}
}
protected override void OnOpen()
{
// 处理客户端连接
Debug.Log("有人连接了");
}
protected override void OnClose(CloseEventArgs e)
{
// 处理客户端断开连接
Debug.Log("有人断开连接了");
}
}
希望这可以帮助你解决问题。
英文:
I can pass data from a different thread to the main thread in c# by using invoke (see below code), but how do I pass data the other direction? trying to pass by reference doesnt seem to be working. Using a global doesnt work either since there are multiple threads.
Ideally I would constantly be updating the local variable in the WS_Client function and then whenever I get a request in mywebsocketserver, it triggers the handle/action which would grab that constantly updated variable and pass it to the mywebsocketserver thread. Mywebsocketserver would then send the data out via the socket.
using UnityEngine;
using WebSocketSharp;
using WebSocketSharp.Server;
using System;
public class OrientationEvent
{
public event Action<Vector3> OnOrientationUpdate;
}
public class WS_Client : MonoBehaviour
{
public static GameObject _cube;
private Action<Vector3> callback;
WebSocket ws;
[SerializeField] float speed = 50.0f;
private Vector3 _orientation;
// Start is called before the first frame update
void Start()
{
_cube = GameObject.Find("MyCube");
if (_cube == null)
{
Debug.Log("couldnt find MyCube at first");
}
WebSocketServer wssv = new WebSocketServer(8080);
Action<Vector3> callback = HandleOrientationUpdate;
MyWebSocketServer wsService = new MyWebSocketServer(callback);
wssv.AddWebSocketService<MyWebSocketServer>("/MyWebSocket", () => wsService);
wssv.Start();
}
public WS_Client(OrientationEvent orientationEvent)
{
callback = HandleOrientationUpdate;
}
private void Update()
{
_cube.transform.rotation = Quaternion.Euler(_orientation);
}
private void HandleOrientationUpdate(Vector3 orientation)
{
Debug.Log("tries to rotate mycube");
Debug.Log(orientation);
// Update the cube's orientation using the data from the event
try
{
_orientation = orientation;
Debug.Log("translates the orientation");
}
catch (Exception ex)
{
Debug.LogError("An error occurred: " + ex.Message);
}
}
}
public class MyWebSocketServer : WebSocketBehavior
{
private Action<Vector3> callback;
public MyWebSocketServer(Action<Vector3> callback)
{
this.callback = callback;
}
protected override void OnMessage(MessageEventArgs e)
{
string data = e.Data;
string[] components = data.Split(' ');
Debug.Log("splits components");
float x = float.Parse(components[0]);
float y = float.Parse(components[1]);
float z = float.Parse(components[2]);
Debug.Log("parses components");
Vector3 orientation = new Vector3(x, y, z);
// Vector3 vector = new Vector3((float)x, (float)y, (float)z);
Debug.Log("puts them in vector3");
try
{
callback?.Invoke(orientation);
Debug.Log("invokes action");
}
catch (Exception ex)
{
Debug.LogError("An error occurred: " + ex.Message);
}
}
protected override void OnOpen()
{
// Handle client connection here
Debug.Log("someone connected");
}
protected override void OnClose(CloseEventArgs e)
{
// Handle client disconnection here
Debug.Log("someone disconnected");
}
}
答案1
得分: 0
如所说,我不认为在这里将任何东西作为 ref
传递是必要的。
我宁愿通过类似于 "MainThreadDispatcher" 的方式来明确执行,例如:
public class WS_Client : MonoBehaviour
{
...
// 线程安全的堆栈
readonly ConcurrentStack<Vector3> lastReceived = new();
void Update()
{
// 在主线程上处理最新接收到的值
if(lastReceived.TryPop(out var value))
{
_cube.transform.rotation = Quaternion.Euler(value);
// 将其他值清除,因为它们已经过时
lastReceived.Clear();
}
}
void HandleOrientationUpdate(Vector3 v)
{
// 这里只是为了在主线程上处理而存储
lastReceived.Push(v);
}
}
在另一个方向上也是一样的,这取决于你的需求,你是需要将它们作为队列来保证可靠性,还是只需要最新的值(在这种情况下,我宁愿像上面那样使用堆栈):
public class MyWebSocketServer : WebSocketBehavior
{
readonly ConcurrentQueue<Vector3> toSend = new();
readonly WaitHandle wait = new AutoResetEvent(false);
public MyWebSocketServer(...)
{
...
var sendThread = new Thread(SendThread);
sendThread.Start();
}
public void Send(Vector3 v)
{
toSend.Enqueue(v);
wait.Set();
}
private void SendThread()
{
while(true)
{
// 允许此线程进入空闲状态,以避免消耗不必要的性能
wait.WaitOne();
// 如果使用队列,请遍历所有项,否则再次只弹出最新的值并删除其他值
while(toSend.TryDequeue(out var v))
{
// 无论最终如何发送您的值,都要使用 ToString 等方法
// 说实话,我更喜欢基于 byte[] 的协议,而不是字符串
// 它会更快解析,也更高效地使用带宽
SubmitVector(v);
}
}
}
}
现在你只需要用最新的值调用 Send
来提交。
你很可能不会想在每一帧都这样做,因为这会严重过载网络带宽。最好使用一个时间间隔,比如 0.2
秒,在接收方进行插值。
英文:
As said I don't see the nead to pass anything as a ref
here.
I would rather do it explicit through a kind of "MainThreadDispatcher" thing like e.g.
public class WS_Client : MonoBehaviour
{
...
// Thread-safe stack
readonly ConcurrentStack<Vector3> lastReceived = new();
void Update()
{
// On the main thread handle the latest received value
if(lastReceived.TryPop(out var value)
{
_cube.transform.rotation = Quaternion.Euler(value);
// erase the others as they are outdated
lastReceived.Clear();
}
}
void HandleOrientationUpdate(Vector3 v)
{
// here only store for being handled on main thread
lastReceived.Push(v);
}
}
The same way in the other direction, here it depends a bit in your needs whether you need those reliable as a queue or again only the latest value (in that case I'd rather use a stack as above)
public class MyWebSocketServer : WebSocketBehavior
{
readonly ConcurrentQueue<Vector3> toSend = new();
readonly WaitHandle wait = new AutoResetEvent(false);
public MyWebSocketServer(...)
{
...
var sendThread = new Thread(SendThread);
sendThread.Start();
}
public void Send(Vector3 v)
{
toSend.Enqueue(v);
wait.Set();
}
private void SendThread()
{
while(true)
{
// Allows this thread to go idle => not eating unnecessary performance
wait.WaitOne();
// if using the queue go through all items, otherwise again only pop the newest value and erase the others
while(toSend.TryDequeue(out var v))
{
// However you finally send your value using ToString etc
// tbh I would prefer a byte[] based protocol over string
// it would be faster to parse and also more efficient bandwidth wise
SubmitVector(v);
}
}
}
So now you only need to call Send
with the newest values to submit.
You most probably will not want to do this every frame as this will overload the network bandwidth a lot. Rather use a time interval like e.g. 0.2
seconds and interpolate at the receiver side.
答案2
得分: 0
我还发现了一个更简单的方法来完成所有操作,只需在一个共享类中共享变量:
using UnityEngine;
using WebSocketSharp;
using WebSocketSharp.Server;
using System;
public class MySharedData
{
public Vector3 handPosition { get; set; }
public Quaternion handRotation { get; set; }
// public Transform handData { get; set; }
}
public class WS_Client : MonoBehaviour
{
MySharedData sharedData = new MySharedData();
WebSocket ws;
public Transform handR;
void Start()
{
handR = GameObject.Find("LeftHandAnchor").GetComponent<Transform>();
// handR = GameObject.Find("RightHandAnchor").GetComponent<Transform>();
WebSocketServer wssv = new WebSocketServer(8080); // 这个websocketserver建立了一个让CPU进行通信的地方
MyWebSocketServer wsService = new MyWebSocketServer(sharedData); // 这实际上是一种websocket行为类型(带有连接到主线程的回调),它会被实现到websocketserver上
wssv.AddWebSocketService<MyWebSocketServer>("/MyWebSocket", () => wsService); // 然后被实现到websocketserver上
wssv.Start();
}
private void Update()
{
sharedData.handPosition = handR.transform.position;
sharedData.handRotation = handR.transform.rotation;
}
}
public class MyWebSocketServer : WebSocketBehavior
{
private MySharedData _sharedData;
public MyWebSocketServer(MySharedData sharedData)
{
this._sharedData = sharedData;
}
protected override void OnMessage(MessageEventArgs e)
{
try
{
string data = e.Data;
Console.WriteLine("Received message: " + e.Data);
Vector3 handPosition = _sharedData.handPosition;
Quaternion handRotation = _sharedData.handRotation;
string responseMessage = string.Format("Position:({0},{1},{2}), Rotation:({3},{4},{5})",
handPosition.x, handPosition.y, handPosition.z,
handRotation.x, handRotation.y, handRotation.z);
Debug.Log(responseMessage);
Send(responseMessage);
}
catch (Exception ex)
{
Debug.LogError("An error occurred: " + ex.Message);
}
}
protected override void OnOpen()
{
// 在这里处理客户端连接
Debug.Log("有人连接上了");
}
protected override void OnClose(CloseEventArgs e)
{
// 在这里处理客户端断开连接
Debug.Log("有人断开连接了");
}
}
英文:
I also found a simpler way to do everything, just share the variable in a shared class:
using UnityEngine;
using WebSocketSharp;
using WebSocketSharp.Server;
using System;
public class MySharedData
{
public Vector3 handPosition { get; set; }
public Quaternion handRotation { get; set; }
// public Transform handData { get; set; }
}
public class WS_Client : MonoBehaviour
{
MySharedData sharedData = new MySharedData();
WebSocket ws;
public Transform handR;
void Start()
{
handR = GameObject.Find("LeftHandAnchor").GetComponent<Transform>();
// handR = GameObject.Find("RightHandAnchor").GetComponent<Transform>();
WebSocketServer wssv = new WebSocketServer(8080); //this websocketserver sets up a place for cpus to talk
MyWebSocketServer wsService = new MyWebSocketServer(sharedData); //this is actually a type of websocketbehavior (with a callback connected to the main thread) which
wssv.AddWebSocketService<MyWebSocketServer>("/MyWebSocket", () => wsService); //then gets implemented onto the websocketserver
wssv.Start();
}
private void Update()
{
sharedData.handPosition = handR.transform.position;
sharedData.handRotation = handR.transform.rotation;
}
}
public class MyWebSocketServer : WebSocketBehavior
{
private MySharedData _sharedData;
public MyWebSocketServer(MySharedData sharedData)
{
this._sharedData = sharedData;
}
protected override void OnMessage(MessageEventArgs e)
{
try
{
string data = e.Data;
Console.WriteLine("Received message: " + e.Data);
Vector3 handPosition = _sharedData.handPosition;
Quaternion handRotation = _sharedData.handRotation;
string responseMessage = string.Format("Position:({0},{1},{2}), Rotation:({3},{4},{5})",
handPosition.x, handPosition.y, handPosition.z,
handRotation.x, handRotation.y, handRotation.z);
Debug.Log(responseMessage);
Send(responseMessage);
}
catch (Exception ex)
{
Debug.LogError("An error occurred: " + ex.Message);
}
}
protected override void OnOpen()
{
// Handle client connection here
Debug.Log("someone connected");
}
protected override void OnClose(CloseEventArgs e)
{
// Handle client disconnection here
Debug.Log("someone disconnected");
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论