#c# #multithreading #asp.net-core
#c# #многопоточность #asp.net-ядро
Вопрос:
У меня есть IHostedService
служба-наследник, которая обрабатывает некоторые данные и сохраняет последнюю часть в переменной.
public class MyService : IHostedService
{
private DataType lastData;
public DataType GetLastData()
{
return lastData;
}
public void ProcessNextPart()
{
...
}
}
У меня также есть контроллер api, который использует DI для вызова MyService
.
Должен ли я использовать lock
или какой-то другой подход lastData
в этом случае? Насколько я понимаю, возможно одновременное чтение (с контроллера) и запись (из сервиса) для этой переменной?
Спасибо.
Комментарии:
1. Для чего это
MyService
нужно? НапримерIHostedService
, он используется для обработки длинной задачи или задачи, которая не связана с запросом. По какой причине вам нужно звонитьMyService
с контроллера? Чего вы хотите достичь в MyService как размещенной службе и чего вы хотите добиться от контроллера с помощью MyService?2. MyService не связан с запросом, он выполняет вычисления в фоновом режиме. Клиент может запросить последнюю часть обработанных данных с помощью контроллера. Итак, суть в многопоточном использовании переменной, где хранится эта последняя часть.
3. если
DataType
это класс, вы в значительной степени в порядке без какого-либо механизма блокировки. Если это структура, то ее запись может быть не атомной, и в этом случае вам следует либо добавить блокировку, либо установить значение
Ответ №1:
Все зависит от того, является ли DataType
это классом или структурой.
Если это класс, то его запись является атомарной, так что это в значительной степени безопасно. Если вы считаете, что вызывающий может использовать это значение в цикле, то вы можете добавить изменяемое чтение, чтобы предотвратить кэширование значения в регистре:
public class MyService : IHostedService
{
private DataType lastData;
public DataType GetLastData()
{
return Volatile.Read(ref lastData);
}
public void ProcessNextPart()
{
lastData = newValue;
}
}
Но, учитывая вариант использования, это звучит крайне маловероятно.
Если DataType
это структура, то это совсем другая история. Если структура больше, чем размер указателя целевой архитектуры (4 байта для x86, 8 байт для x64), то ее запись не является атомарной. Даже если структура достаточно мала, я не рекомендую полагаться на нее, так как позже вы можете добавить больше полей и нарушить этот код. Таким образом, у вас есть два решения: либо использовать блокировку, либо указать значение.
Использование блокировки:
public class MyService : IHostedService
{
private readonly object syncRoot = new object();
private DataType lastData;
public DataType GetLastData()
{
lock (syncRoot)
return lastData;
}
public void ProcessNextPart()
{
lock (syncRoot)
lastData = newValue;
}
}
Упаковка значения:
using System.Runtime.CompilerServices;
public class MyService : IHostedService
{
private readonly object syncRoot = new object();
private StrongBox<DataType> lastData;
public DataType GetLastData()
{
return lastData.Value;
}
public void ProcessNextPart()
{
lastData = new StrongBox<DataType>(newValue);
}
}
Комментарии:
1. Спасибо вам за ответ. Является ли первый случай истинным для List<T> и обнуляемым (например, int?)?
2.
List<T>
это класс, так что это первый случай.Nullable<T>
является структурой, поэтому второй случай