Предположим, вы используете реализацию сеанса по умолчанию, предлагаемую ASP. Net Core.
В терминах HttpContext.Session
:
HttpContext.Session
возвращает экземпляр DistributedSession
, который внутренне использует Dictionary<TKey, TValaue>
. Словарь не является потокобезопасным, поэтому, если вы обращаетесь к Session
из нескольких потоков (например, Task.Run
), это может привести к неожиданным результатам.
С точки зрения сеанса для различных запросов:
В ASP. Net Ядро, Session
происходит от ISessionStore
, который имеет кратковременное время жизни . Значение Session
объект не является общим для запросов . Поэтому, если у вас есть одновременные запросы, каждый из которых будет иметь свой собственный объект Session
.
С точки зрения условия гонки:
Реализация по умолчанию сеанса читает / записывает состояние сеанса с / на .AspNetCore.Session
cook ie. Это может вызывать условия состязания.
Поскольку Session
для каждого клиента, вы можете иметь условия состязания , когда у вас есть одновременные запросы от одного и того же клиента, которые касаются одинаковых бит тот же повар ie / состояние сеанса . Состояние гонки, однако, не из-за Session
на стороне сервера. Это на самом деле вызвано управлением ie на стороне клиента.
Состояние сеанса неблокирующее. Если два запроса одновременно пытаются изменить содержимое сеанса, последний запрос переопределяет первый.
Рассмотрим этот пример:
Скажем, у вас есть действие контроллера, которое устанавливает Session
с предоставленным value
, а другое действие контроллера извлекает значение из Session
и возвращает его в теле:
[HttpGet]
[Route("create")]
public IActionResult CreateSession([FromQuery]string value)
{
HttpContext.Session.SetString("key", value);
return Ok();
}
[HttpGet]
[Route("get")]
public IActionResult ReturnSession([FromQuery] string expected)
{
var actual = HttpContext.Session.GetString("key");
return Ok(new { actual, expected });
}
Если вы тестируете эти действия с HttpClient
:
async Task TestSession(HttpClient client, string str)
{
await client.GetAsync($"https://localhost:5001/session/create?value={str}");
var r = await client.GetAsync($"https://localhost:5001/session/get?expected={str}");
var session = await r.Content.ReadAsStringAsync();
Console.WriteLine(session);
}
using (var client = new HttpClient())
{
await TestSession(client, "abc");
}
Вывод должен выглядеть следующим образом:
{"actual":"abc","expected":"abc"}
Возникает проблема при одновременных запросах от одного и того же клиента:
using (var client = new HttpClient())
{
var tasks = new List<Task>();
for (var i = 0; i < 10; i++)
{
var str = i.ToString();
tasks.Add(Task.Run(() => TestSession(client, str)));
}
await Task.WhenAll(tasks);
}
Вывод выглядит следующим образом:
{"actual":"2","expected":"1"}
{"actual":"3","expected":"6"}
{"actual":"4","expected":"5"}
{"actual":"4","expected":"3"}
{"actual":"4","expected":"2"}
{"actual":"8","expected":"4"}
{"actual":"7","expected":"8"}
{"actual":"7","expected":"7"}
{"actual":"9","expected":"0"}
{"actual":"9","expected":"9"}
В приведенном выше случае состояние сеанса было изменено с помощью запроса 3 , между create и get из request 6 , то есть запрос 6 не может правильно видеть состояние сеанса.
Чтобы избежать этой проблемы, вы можете использовать разные HttpClient
для каждого пакета.