Согласно параллелизму и государственной библиотеке для .NET, сияет в сетевых вызовах.
Пакет Nuget доступен по адресу https://www.nuget.org/packages/serviceconcurrency/
Библиотека для обработки параллелизма и состояния, в первую очередь для кода, который вызывает веб -сервисы.
Во -первых, это мешает ненужным звонкам. Когда звонок с соответствующими аргументами уже находится в полете, одновременный абонент припаркован и возобновится, когда исходной запрос завершится. Если аргумент является коллекцией, организации, уже в полете, разбросаны.
Во -вторых, он кэширует запросы на возвращение стоимости. Когда для любого данного запроса существует кэшированное значение, оно будет возвращено вместо вызова. Это значение можно получить в любое время, предотвращая необходимость дополнительных полей поддержки в вашем коде. Если аргумент является коллекцией, кэшированные сущности лишены аргумента.
Эта библиотека использует imemorycache для внутреннего кэширования данных.
Вот несколько вещей ServiceConcurrency может справиться с вами. Более подробные примеры можно найти в последующих разделах этого документа.
GetSessionToken () вернет кэшированное значение после того, как был сделан первый вызов. Если getsessionToken () называется одновременно, в то время как он еще не имеет кэшированного значения, только один вызов fetchSessionToken () когда -либо будет сделан, а другие одновременные вызовы будут давать до тех пор, пока не будет доступно значение.
private ServiceConcurrency . ReturnsValue < string > sessionTokenState =
new ServiceConcurrency . ReturnsValue < string > ( ) ;
public async Task < string > GetSessionToken ( )
{
return await this . sessionTokenState . Execute ( this . FetchSessionToken ) ;
}То же, что и выше, но сбор аргументов лишится из значений, которые уже кэшируются или в полете. Таким образом, FetchuserProfiles () никогда не будет вызвана с идентификатором более одного раза.
private ServiceConcurrency . TakesEnumerationArgReturnsValue < Guid , UserProfile > userProfilesState =
new ServiceConcurrency . TakesEnumerationArgReturnsValue < Guid , UserProfile > ( ) ;
public async Task < IEnumerable < UserProfile > > GetUserProfiles ( IEnumerable < Guid > userProfileIds )
{
return await this . userProfilesState . Execute (
this . FetchUserProfiles ,
( guid , results ) => results . SingleOrDefault ( t => t . Id == guid ) ,
userProfileIds
) ;
} В этой библиотеке четыре класса:
Noargnavalue
Предотвращает одновременные вызовы, разрешая только один активный вызов за раз, когда одновременные вызовы будут ждать, пока активный вызов завершит.
Returnsvalue <plaalue>
Будет сделан только один вызов, и последующие вызовы принесут значение из кеша. Также предотвращает любые одновременные вызовы, позволяя только один активный вызов за раз, когда параллельные вызовы будут ждать активного вызова.
TakesArg <Targ>
Предотвращает одновременные вызовы при обмене тем же аргументом, разрешая только один активный вызов за один раз, когда параллельные вызовы будут ждать активного вызова.
TakesArgreTurnsValue <targ, Tvalue>
Для данного аргумента будет сделан только один вызов, и последующие вызовы принесут значение из кэша. Также предотвращает любые параллельные вызовы, позволяя только один активный вызов за один раз, когда параллельные вызовы будут ждать активного вызова.
TakesEnumerationArg <Targ>
Одновременные вызовы будут выполняться только один раз для данного аргумента в коллекции аргументов.
Сбор аргументов лишен значений, для которых операция уже находится в полете.
Таким образом, одновременно призыв к [«a», «b», «c»] и [«b», «c», «d»] приведет к одному вызову с [«a», «b», «c»] и одним с ["d"].
TakesEnumerationArgreTurnSValue <TARG, TVALUE>
Первый вызов и любые одновременные вызовы будут выполняться только один раз для данного аргумента в сборе аргументов. Последующие вызовы принесут значение из кэша.
Сбор аргументов лишен значений, для которых существует кэшированное значение или операция уже находится в полете.
Таким образом, одновременно призыв к [«a», «b», «c»] и [«b», «c», «d»] приведет к одному вызову с [«a», «b», «c»] и одним с ["d"]. В следующий раз, когда в следующий раз «A», «B», «C» или «D» вызовет, он будет лишен коллекции, и значение для нее будет извлечено из кеша.
Все объекты ServiceConcurency имеют конструктор без параметра, и в этом случае будет создан внутренний imemoryCache.
Все объекты ServiceConcorrency, которые возвращают значения, также имеют конструктор, который принимает объект imemoryCache, если вы хотите поделиться кэшем со всеми объектами ServiceConcurrency. Если вы это сделаете, пожалуйста, обязательно используйте уникальные ключи на записях.
T Execute ( .. . )Выполняет конкретный запрос. Вы предоставляете обратный вызов для выполнения внешнего звонка. См. Примеры для получения дополнительной информации, так как аргументы и типы возврата являются специфичными для типа ServiceConcorrency. Полученное значение кэшируется и предоставляется вам в вашем обратном вызове.
void Reset ( )Сбрасывает внутреннее состояние, IE состояние для вызовов в процессе и внутреннего кэша (в случае возврата объекта ServiceConcorrency возвращает значения).
bool IsExecuting ( )Возвращает, выполняет ли объект ServiceConcorrency в настоящее время выполнять конкретный запрос.
Следующие методы доступны только в объектах ServiceConcurrency, которые возвращают значения.
void ResetCache ( )Сбрасывает внутренний кеш. Также называется из сброса ().
bool TryGetValue ( TArg key , out TValue value )Получает значение из кеша.
void Set ( TArg key , TValue value )Сеттер кэша в редком случае, который вам может потребоваться манипулировать кэшем вручную.
void Remove ( TArg key )Удаляет значение из кеша.
bool ContainsKey ( TArg key )Проверяет, существует ли запись в кэше.
TValue this [ TArg key ]Оператор массива для настройки и получения кеша. Бросает KeyNotFoundException в Getter, если запись не существует.
IEnumerator < KeyValuePair < TArg , TValue > > GetEnumerator ( )
IEnumerator IEnumerable . GetEnumerator ( )Перечисления для внутреннего кеша. Позволяет использовать объекты в операторах Foreach и LINQ.
void Dispose ( ) Утилизирует внутренний кэш в случае, если Bool IsCacheShared является ложным. Если это общий кэш, он просто вызовет ResetCache . Вместо этого.
TValue Value ;Только в rewardsvalue <value>. Это единственный кэшированный объект.
Следующие свойства доступны только в объектах ServiceConconcy, которые возвращают значения.
MemoryCacheEntryOptions CacheEntryOptions ;Эти параметры внутренне используются, когда значение кэшируется. Редактирование их позволяет устанавливать срок действия, размеры кэша и т. Д. См. MemoryCacheoptions для получения более подробной информации.
bool IsCacheShared ;Только для получения. Обозначает, является ли кэш обмен или нет.
Execute () принимает необязательный преобразователь значений, который может изменить извлеченное значение перед возвратом и кэшированием его. Это доступно только в объектах ServiceConcurency, которые возвращают значения.
private ServiceConcurrency . ReturnsValue < string > lastName =
new ServiceConcurrency . ReturnsValue < string > ( ) ;
private const string FirstName = "John" ;
public async Task < string > GetFullName ( )
{
return await this . lastName . Execute (
this . GetLastName ,
( lastName ) => $ " { FirstName } { lastName } " ;
) ;
}Объекты ServiceConcurrency также принимают дополнительный параметр, который объявляет тип значения внутреннего запроса. Когда это будет указано, преобразователь значений также будет преобразовать между типом источника и типом назначения. Это полезно, если запрос, вызванный execute (), имеет другой тип, чем желаемое поле поддержки.
// FetchChatRooms() returns an IEnumerable<ChatRoom>, chatRoomMap handles it as Dictionary<Guid, ChatRoom>
private ServiceConcurrency . ReturnsValue < IEnumerable < ChatRoom > , Dictionary < Guid , ChatRoom > > chatRoomMap =
new ServiceConcurrency . ReturnsValue < IEnumerable < ChatRoom > , Dictionary < Guid , ChatRoom > > ( ) ;
public async Task < IEnumerable < ChatRoom > > UpdateChatRooms ( )
{
return ( await this . chatRoomMap . Execute (
this . FetchChatRooms ,
( chatRooms ) => chatRooms . ToDictionary ( t => t . Id , t => t ) // cache as id -> chatroom map
) ) ? . Values ;
}
public ChatRoom GetChatRoom ( Guid chatRoomId )
{
ChatRoom chatRoom ;
if ( this . chatRoomMap . Value . TryGetValue ( chatRoomId , out chatRoom ) ) // value is Dictionary<Guid, ChatRoom>
return chatRoom ;
return null ;
} using System ;
using System . Collections . Generic ;
using System . Threading . Tasks ;
using System . Linq ;
public class MyService : IDisposable
{
////////////////////////////////////////////////////////////////////////////
// NoArgNoValue example
private ServiceConcurrency . NoArgNoValue simpleCallState =
new ServiceConcurrency . NoArgNoValue ( ) ;
// Concurrent calls won't invoke the callback multiple times - only the first
// call will invoke it, and the rest will wait until it finishes.
public async Task CallSomething ( )
{
await this . simpleCallState . Execute (
async ( ) =>
{
Console . WriteLine ( "CallSomething call in flight" ) ;
await Task . Delay ( 100 ) ;
}
) ;
}
////////////////////////////////////////////////////////////////////////////
// ReturnsValue example
private ServiceConcurrency . ReturnsValue < string > returnsValueState =
new ServiceConcurrency . ReturnsValue < string > ( ) ;
// Only one call will be made and subsequent calls will fetch the value from
// cache. Also prevents any concurrent calls from occurring, by allowing only
// one active call at a time, where concurrent calls will wait for the active
// call to finish.
public async Task < string > FetchSomething ( )
{
return await this . returnsValueState . Execute (
async ( ) =>
{
Console . WriteLine ( "FetchSomething call in flight" ) ;
await Task . Delay ( 100 ) ;
return "Hello world!" ;
}
) ;
}
////////////////////////////////////////////////////////////////////////////
// TakesArg example
private ServiceConcurrency . TakesArg < Guid > takesArgState =
new ServiceConcurrency . TakesArg < Guid > ( ) ;
// Prevents concurrent calls when sharing the same argument, by allowing only
// one active call at a time per argument, where concurrent calls will wait for
// the active call to finish.
public async Task PostSomething ( Guid someId )
{
await this . takesArgState . Execute (
async ( Guid id ) =>
{
Console . WriteLine ( $ "PostSomething call in flight, for argument { id } " ) ;
await Task . Delay ( 100 ) ;
} ,
someId
) ;
}
////////////////////////////////////////////////////////////////////////////
// TakesArgReturnsValue example
private ServiceConcurrency . TakesArgReturnsValue < Guid , string > takesArgReturnsValueState =
new ServiceConcurrency . TakesArgReturnsValue < Guid , string > ( ) ;
// For a given argument, only one call will be made and subsequent calls will
// fetch the value from cache. Also prevents any concurrent calls from occurring,
// by allowing only one active call at a time per argument, where concurrent
// calls will wait for the active call to finish.
public async Task < string > FetchSomethingFor ( Guid someId )
{
return await this . takesArgReturnsValueState . Execute (
async ( Guid id ) =>
{
Console . WriteLine ( $ "FetchSomethingFor call in flight, for argument { id } " ) ;
await Task . Delay ( 100 ) ;
return $ "The guid is { id } " ;
} ,
someId
) ;
}
////////////////////////////////////////////////////////////////////////////
// TakesEnumerationArg example
private ServiceConcurrency . TakesEnumerationArg < Guid > takesEnumerationArgState =
new ServiceConcurrency . TakesEnumerationArg < Guid > ( ) ;
// Concurrent calls will execute only once for a given argument in the argument
// collection.
//
// The argument collection is stripped of values for which an operation is already
// in flight.
//
// So simultaneously calling with ["A", "B", "C"] and ["B", "C", "D"] will result
// in one call with ["A", "B", "C"] and one with ["D"].
public async Task PostCollection ( IEnumerable < Guid > someIds )
{
await this . takesEnumerationArgState . Execute (
async ( IEnumerable < Guid > ids ) =>
{
Console . WriteLine ( $ "PostCollection call in flight, for arguments { ids . Select ( t => t ) } " ) ;
await Task . Delay ( 100 ) ;
} ,
someIds
) ;
}
////////////////////////////////////////////////////////////////////////////
// TakesEnumerationArgReturnsValue example
private ServiceConcurrency . TakesEnumerationArgReturnsValue < Guid , ExampleClass > takesEnumArgReturnsValueState =
new ServiceConcurrency . TakesEnumerationArgReturnsValue < Guid , ExampleClass > ( ) ;
public class ExampleClass
{
public Guid Id { get ; set ; }
}
// The first call, and any concurrent calls, will execute only once for a
// given argument in the argument collection. Subsequent calls will fetch value
// from cache.
//
// The argument collection is stripped of values for which a cached value exists
// or an operation is alraedy in flight.
//
// So simultaneously calling with ["A", "B", "C"] and ["B", "C", "D"] will
// result in one call with ["A", "B", "C"] and one with ["D"].
// The next time "A", "B", "C" or "D" is called with, it will be stripped from
// the collection and a value for it will be fetched from the cache.
public async Task < IEnumerable < ExampleClass > > FetchCollectionForThese ( IEnumerable < Guid > someIds )
{
return await this . takesEnumArgReturnsValueState . Execute (
async ( IEnumerable < Guid > ids ) =>
{
Console . WriteLine ( $ "FetchCollectionForThese call in flight, for arguments { ids . Select ( t => t ) } " ) ;
await Task . Delay ( 100 ) ;
return ids . Select ( t => new ExampleClass ( )
{
Id = t
} ) ;
} ,
// a mapper from arg to result is required - should return the corresponding value for the passed argument
( Guid id , IEnumerable < ExampleClass > result ) => result . SingleOrDefault ( t => t . Id == id ) ,
someIds
) ;
}
void Dispose ( )
{
this . simpleCallState . Dispose ( ) ;
this . returnsValueState . Dispose ( ) ;
this . takesArgState . Dispose ( ) ;
this . takesArgReturnsValueState . Dispose ( ) ;
this . takesEnumerationArgState . Dispose ( ) ;
this . takesEnumArgReturnsValueState . Dispose ( ) ;
}
}