2015-12-02 4 views
5

Я работаю над приложением веб-API, которое подключается к бэкэнд-системе через API. Одна из проблем работы с API заключается в том, что для него требуется поддерживать удаленный сеанс, который совместно используется всеми потоками/запросами для веб-API. Сессия заканчивается каждые несколько часов и должна быть обновлена ​​через логин..NET Многопоточный доступ к сеансу общего входа

упрощенная версия моей текущей реализации ниже:

private static Object loginLock = new Object(); 

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

// Do stuff with the API 

При высокой параллельной нагрузке, когда требуется Логин, потоки получить сложены в замок и пропускаются по одному за раз при успешной логин, который вызывает узкое место в производительности.

Я ищу, чтобы заблокировать все потоки, когда требуется логин, но позвольте им пройти через успешный вход в систему.

Есть ли лучший образец для решения этой проблемы? Похоже, что Googling указывает, что ReaderWriterLockSlim или Monitor Wait/Pulse/PulseAll могут быть лучшими кандидатами, чем стандартный замок.

+0

Из любопытства, что происходит, когда сеанс истекает после проверки Api.IsLoggedIn, но до фактического использования Api? –

+0

Ошибка API вызовет ошибку «no valid session», но мы этого не видели (пока). – WayneC

+3

В чем же проблема? Запросы в очереди кажутся неизбежными. Но как только сеанс становится доступным, очередь должна очищаться очень быстро. Как миллион потоков в секунду или около того. – 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)' this никогда не будет true, так как 'CompareExchange' возвращает исходное значение поля, и это какая-то другая задача или' null' для первого потока. И если какой-то поток будет 'Exchange'' _loginLock' с 'null', все остальные в этом случае получат' null' с 'CompareExchange', поэтому' result.Wait(); 'будет сбой. – VMAtm

+0

«Здесь все еще небольшое количество расизма», это интересный способ поместить вещи. Раскость - это двоичная вещь, она есть или нет. – Andrey

+0

Я бы использовал Lazy для этого вместо задач. – 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(); 
Смежные вопросы