2012-06-20 31 views
5

我試圖連接一個AsyncController,以便當用戶點擊訂單頁上的訂單保存時,所有查看同一訂單的用戶都應該收到訂單已更改的通知。我實現這個的方法是在訂單頁面上長時間輪詢ajax請求,但如何使一個可擴展的AsyncController來處理這個問題對我來說並不明顯。如何在MVC中正確地進行長輪詢3

所以這就是我到目前爲止,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"); 
    } 
} 

我看了一下How to do long-polling AJAX requests in ASP.NET MVC?。我想了解關於這段代碼的所有細節,但據我所知,這段代碼阻塞了線程池中的每個工作線程,據我所知最終會導致線程匱乏。

那麼,我應該如何以一個很好的,可擴展的方式來實現這個?請記住,我不希望再使用任何第三方組件,我想深入瞭解如何正確實施此方案。

回答

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")); 
    } 
}