1

SignalR 无法拥有返回值的客户端方法。所以我正在尝试创建一个帮助类来实现这一点。

所以这就是我想要做的:

  1. 服务器端:调用客户端方法并提供唯一的请求idClient(clientId).GetValue(requestId)
  2. 服务器端:保存 requestId 并等待回答使用ManualResetEvent
  3. 客户端:内部void GetValue(Guid requestId)调用服务器方法hubProxy.Invoke("GetValueFinished", requestId, 10)
  4. 服务器端:通过requestId找到等待方法=>设置返回值=>设置信号
  5. 服务器端:方法不再等待 vorManualResetEvent并返回检索到的值。

不幸的是,我能够让它工作。这是我的代码:

public static class MethodHandler
{
    private static ConcurrentDictionary<Guid, ReturnWaiter> runningMethodWaiters = new ConcurrentDictionary<Guid,ReturnWaiter>();

    public static TResult GetValue<TResult>(Action<Guid> requestValue)
    {
        Guid key = Guid.NewGuid();
        ReturnWaiter returnWaiter = new ReturnWaiter(key);
        runningMethodWaiters.TryAdd(key, returnWaiter);
        requestValue.Invoke(key);
        returnWaiter.Signal.WaitOne();
        return (TResult)returnWaiter.Value;
    }

    public static void GetValueResult(Guid key, object value)
    {
        ReturnWaiter waiter;
        if (runningMethodWaiters.TryRemove(key, out waiter))
        {
            waiter.Value = value;
        }
    }
}

internal class ReturnWaiter 
{
    private ManualResetEvent _signal  = new ManualResetEvent(false);
    public ManualResetEvent Signal { get { return _signal; } }
    public Guid Key {get; private set;}

    public ReturnWaiter(Guid key)
    {
        Key = key;
    }

    private object _value;
    public object Value 
    {
        get { return _value; }
        set 
        {
            _value = value;
            Signal.Set();
        }
    }
}

使用这个MethodHandler类我需要有两个方法服务器端:

public int GetValue(string clientId)
{
    return MethodHandler.GetValue<int>(key => Clients(clientId).Client.GetValue(key));
}

public void GetValueResult(Guid key, object value)
{
    MethodHandler.GetValueResult(key, value);
}

客户端实现是这样的:

// Method registration
_hubProxy.On("GetValue", new Action<Guid>(GetValue));

public void GetValue(Guid requestId)
{
    int result = 10;
    _hubConnection.Invoke("GetValueResult", requestId, result);
}

问题:
如果我打电话给服务器端GetValue("clientid")。不会调用客户端方法。如果我注释掉returnWaiter.Signal.WaitOne();,则调用客户端GetValue并调用服务器端GetValueResult。但是当然这次方法已经返回了。

我认为这与,ManualResetEvent但即使使用while(!returnWaiter.HasValue) Thread.Sleep(100);也无法解决此问题。

任何想法如何解决这个问题?

提前致谢!

4

2 回答 2

3

首先,我认为,与其在如何使其同步方面寻求帮助,不如告诉我们您正在尝试做什么,这样我们就可以建议一种适当的方法来做到这一点。

你没有展示你的MethodHandler::Retrieve方法,但我几乎可以猜到它的样子,这甚至不是真正的问题。我必须以最好的方式告诉你,这是一个非常糟糕的主意。它永远不会扩展。这仅适用于单个 SignalR 服务器实例,因为您依赖机器特定的资源(例如 后面的内核对象ManualResetEvent)来提供阻塞。也许您不需要扩展超过一台服务器即可满足您的要求,但是即使在单台服务器上,这仍然是一种可怕的资源浪费。

您实际上走在正确的轨道上,客户端使用requestId作为相关标识符进行回调。为什么你不能使用这种相关性来恢复你在服务器端中间的任何进程的逻辑执行?这样,在等待将消息传递给客户端、进行处理然后将后续消息(GetValueResult在您的示例中)发送回服务器实例时,不会保留任何资源。

于 2013-07-12T21:22:02.757 回答
0

问题解决了:

该问题仅出现在Hub.OnConnected和中Hub.OnDisconnected。我没有确切的解释为什么,但可能这些方法必须能够在它处理您对客户端的方法调用之前完成。

所以我改变了代码:

public override Task OnConnected()
{
    // NOT WORKING
    Debug.Print(MethodHandler.GetValue<int>(key => Clients(Context.ConnectionId).Client.GetValue(key)));

    // WORKING
    new Thread(() => Debug.Print(MethodHandler.GetValue<int>(key => Clients(Context.ConnectionId).Client.GetValue(key)))).Start();

    return base.OnConnected();
}
于 2013-07-25T14:03:59.900 回答