如何在MVC 3中正确进行长轮询

Osk*_*erg 5 asp.net-mvc asynchronous long-polling

我正在尝试连接AsyncController,以便当用户单击订单页面上的订单上的保存时,查看相同订单的所有用户都应该收到订单已更改的通知.我实现这一点的方法是在订单页面上进行长轮询ajax请求,但是如何制作可扩展的AsyncController来处理这个问题对我来说并不明显.

所以这就是我到目前为止,ID是指示更改或轮询更改的订单的ID.

public class MessageController : AsyncController
{
    static readonly ConcurrentDictionary<int, AutoResetEvent> Events = new ConcurrentDictionary<int, AutoResetEvent>();

    public ActionResult Signal(int id)
    {
        AutoResetEvent @event;
        if (Events.TryGetValue(id, out @event))
            @event.Set();

        return Content("Signal");
    }

    public void WaitAsync(int id)
    {
        Events.TryAdd(id, new AutoResetEvent(false));

        // TODO: This "works", but I should probably not block this thread.
        Events[id].WaitOne();
    }

    public ActionResult WaitCompleted()
    {
        return Content("WaitCompleted");
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经看过如何在ASP.NET MVC中进行长轮询AJAX请求?.我试图了解有关此代码的所有细节,但据我了解此代码,它阻止了线程池中的每个工作线程,据我所知,最终会导致线程饥饿.

那么,我该如何以一种漂亮,可扩展的方式实现它呢?请记住,我不想再使用任何第三方组件,我希望能够很好地理解如何正确实现此方案.

Osk*_*erg 3

实际上,我能够在不阻塞工作线程的情况下实现这一点,我缺少的是 ThreadPool.RegisterWaitForSingleObject。

public class ConcurrentLookup<TKey, TValue>
{
    private readonly Dictionary<TKey, List<TValue>> _lookup = new Dictionary<TKey, List<TValue>>();

    public void Add(TKey key, TValue value)
    {
        lock (_lookup)
        {
            if (!_lookup.ContainsKey(key))
                _lookup.Add(key, new List<TValue>());

            _lookup[key].Add(value);
        }
    }

    public List<TValue> Remove(TKey key)
    {
        lock (_lookup)
        {
            if (!_lookup.ContainsKey(key))
                return new List<TValue>();

            var values = _lookup[key];
            _lookup.Remove(key);

            return values;
        }
    }
}

[SessionState(SessionStateBehavior.Disabled)]
public class MessageController : AsyncController
{
    static readonly ConcurrentLookup<int, ManualResetEvent> Events = new ConcurrentLookup<int, ManualResetEvent>();

    public ActionResult Signal(int id)
    {
        foreach (var @event in Events.Remove(id))
            @event.Set();

        return Content("Signal " + id);
    }

    public void WaitAsync(int id)
    {
        AsyncManager.OutstandingOperations.Increment();

        var @event = new ManualResetEvent(false);

        Events.Add(id, @event);

        RegisteredWaitHandle handle = null;
        handle = ThreadPool.RegisterWaitForSingleObject(@event, (state, timeout) => 
        {
            handle.Unregister(@event);
            @event.Dispose();

            AsyncManager.Parameters["id"] = id;
            AsyncManager.Parameters["timeout"] = timeout;
            AsyncManager.OutstandingOperations.Decrement();
        }, null, new TimeSpan(0, 2, 0), false);
    }


    public ActionResult WaitCompleted(int id, bool timeout)
    {
        return Content("WaitCompleted " + id + " " + (timeout? "Timeout" : "Signaled"));
    }
}
Run Code Online (Sandbox Code Playgroud)