2015-12-02 34 views
5

我正在研究通過API連接到後端系統的Web API應用程序。使用API​​的一個挑戰是它需要維護一個遠程會話,該會話在Web API的所有線程/請求之間共享。會話每隔幾個小時到期,需要通過登錄進行「刷新」。.NET多線程訪問共享登錄會話

我目前實現的簡化版本如下:

private static Object loginLock = new Object(); 

if(!Api.IsLoggedIn) 
{ 
    lock(loginLock) 
    { 
     if(!Api.IsLoggedIn) 
     { 
      Api.Login(); 
     } 
    } 
} 

// Do stuff with the API 

在高併發負載,需要登錄時,線程都在鎖堆積起來,並通過一個在成功都讓在同一時間登錄導致性能瓶頸。

我在尋找的是一種在需要登錄時阻止所有線程的方法,但是在成功登錄後讓它們全部通過。

解決這個問題有更好的模式嗎?谷歌搜索似乎表明,ReaderWriterLockSlimMonitor Wait/Pulse/PulseAll可能比標準鎖更好的候選人。

+0

出於好奇,當會話在'Api.IsLoggedIn'檢查後但在實際使用Api之前過期會發生什麼? –

+0

API調用會失敗,出現「無效會話」錯誤,但我們還沒有看到這種情況(到目前爲止)。 – WayneC

+3

究竟是什麼問題?排隊的請求似乎是不可避免的。但是,一旦會話變得可用,隊列應該非常快地清除。就像每秒100萬線程一樣。 – usr

回答

2

這是一個不尋常的問題,我不知道有什麼內置的具體解決這個問題。請記住,我已經在幾分鐘內敲了這個,所以我絕對建議不要使用這個,直到很多人有機會看它並指出它的缺陷,這就是我認爲:

private Task _loginLock = null; 

public void DoLoggedInCheck() 
{ 
    if (!Api.IsLoggedIn) 
    { 
     var tcs = new TaskCompletionSource<int>(); 
     var tsk = tcs.Task; 
     var result = Interlocked.CompareExchange(ref _loginLock, tsk, null); 
     if (result == null) 
     { 
      if (!Api.IsLoggedIn) 
      { 
       Api.Login(); 
      } 
      Interlocked.Exchange(ref _loginLock, null); 
      tcs.SetResult(1); 
     } 
     else 
     { 
      result.Wait(); 
     } 
    } 
} 

邏輯在於,出於所有的發現,一個需要登錄的螺紋的,它們都競爭(經由CompareExchange)是自願來解決這個問題的一個。其中一人贏得勝利並完成任務,剩下的只是等待勝利者發出成功信號。

這裏仍然有少量的突變,但應該很少見。

+0

'if(result == tsk)'這永遠不會是真的,因爲'CompareExchange'返回字段的原始值,並且它是其他任務或者是null的第一個線程。如果某個線程將用'null'交換''_loginLock',在這種情況下所有其他線程將''null'與'CompareExchange',所以'result.Wait();'會崩潰。 – VMAtm

+0

「這裏仍然有少量的不合適」,這是一種有趣的放置方式。 Raciness是二元的東西,它是否存在或不存在。 – Andrey

+0

我會用懶惰代替任務。 – usr

0

如果只想使用工作線程來解析它,我沒有看到任何其他方法,但有一個關鍵部分,這是關鍵部分的性質,一次只有一個線程可以傳遞它。完全的其他方法是將關鍵部分的委託處理分隔線程。我不確定它會更高效(最可能會慢得多),但一旦Api登錄,就不會有交通堵塞。

 private static AutoResetEvent requestLogin = new AutoResetEvent(); 
     private static ManualResetEvent responseLogin = new ManualResetEvent(); 

     //Worker thread: 

     if(!Api.IsLoggedIn) 
     { 
      requestLogin.Set(); 
      responseLogin.WaitOne(); 
     } 

     //Login thread 

     requestLogin.WaitOne(); 
     if(!Api.IsLoggedIn) 
     { 
      Api.Login(); 
     } 
     responseLogin.Set();