.NETの並行性と状態ライブラリは、ネットワーク呼び出しで輝いています。
nugetパッケージhttps://www.nuget.org/packages/serviceconcurrency/で入手可能
主にWebサービスに呼びかけるコード用の並行性と状態を処理するためのライブラリ。
まず、不必要な呼び出しが起こるのを防ぎます。一致する引数が既に飛行中の呼び出しが既に飛行中である場合、同時発信者は駐車され、発信元のリクエストが終了すると再開されます。引数がコレクションの場合、すでに飛行中のエンティティは剥奪されます。
第二に、リクエストを返す価値の状態をキャッシュします。特定の要求に対してキャッシュされた値が存在する場合、呼び出しが行われる代わりに返されます。この値はいつでもアクセスでき、コード内の追加のバッキングフィールドの必要性を妨げることができます。引数がコレクションの場合、キャッシュされたエンティティは議論から剥奪されます。
このライブラリは、データの内部キャッシングにiMemorycacheを使用しています。
ServiceConcurrencyが処理できることがいくつかあります。このドキュメントの後のセクションには、より詳細な例があります。
GetSessionToken()は、最初の呼び出しが行われた後、キャッシュ値を返します。 getSessionToken()が同時に呼び出された場合、まだ値をキャッシュしていない場合、FetchSessionToken()への1つのコールのみが行われ、他の同時コールは値が利用可能になるまで生成されます。
private ServiceConcurrency . ReturnsValue < string > sessionTokenState =
new ServiceConcurrency . ReturnsValue < string > ( ) ;
public async Task < string > GetSessionToken ( )
{
return await this . sessionTokenState . Execute ( this . FetchSessionToken ) ;
}上記と同じですが、引数コレクションには、すでにキャッシュされているか飛行中の値が剥がされています。したがって、FetchUserProfiles()は、IDで複数回呼び出されることはありません。
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
) ;
} このライブラリには4つのクラスがあります。
noargnovalue
一度に1つのアクティブコールのみを許可することにより、同時コールを防ぎ、同時コールがアクティブコールが終了するのを待ちます。
returnsValue <TValue>
1つの呼び出しのみが行われ、その後のコールはキャッシュから値を取得します。また、一度に1つのアクティブコールのみを許可することにより、同時コールがアクティブコールが終了するのを待つことにより、同時コールが発生するのを防ぎます。
Takearg <targ>
同じ引数を共有するときに同時コールを防ぎます。引数ごとに1回のアクティブコールのみを許可することにより、同時コールがアクティブコールが終了するのを待つことにより。
takeargreturnsvalue <targ、tvalue>
特定の引数では、1つの呼び出しのみが行われ、その後のコールがキャッシュから値を取得します。また、引数ごとに1回のアクティブコールのみを許可することにより、同時コールが発生するのを防ぎます。
takeNumerationArg <Targ>
同時コールは、引数コレクションの特定の引数に対して1回だけ実行されます。
引数コレクションには、操作がすでに飛行中の値が剥奪されます。
したがって、同時に["a"、 "b"、 "c"]と["b"、 "c"、 "d"で呼び出します。
takeNumerationArgreturnSvalue <targ、tvalue>
最初の呼び出し、および任意の同時呼び出しは、引数コレクションの特定の引数に対して一度だけ実行されます。後続の呼び出しは、キャッシュから値を取得します。
引数コレクションには、キャッシュされた値が存在する値が削除されているか、操作がすでに飛行中です。
したがって、同時に["a"、 "b"、 "c"]と["b"、 "c"、 "d"で呼び出します。次回「A」、「B」、「C」、または「D」が呼び出され、コレクションから剥がされ、その値はキャッシュからフェッチされます。
すべてのServiceConcurrencyオブジェクトには、パラメーターのないコンストラクターがあり、この場合は内部のiMemorycacheが作成されます。
値を返すすべてのServiceConcurrencyオブジェクトには、すべてのServiceConcurrencyオブジェクトとキャッシュを共有する場合に備えて、iMemorycacheオブジェクトを受け入れるコンストラクターもあります。そうした場合は、エントリに一意のキーを使用してください。
T Execute ( .. . )特定のリクエストを実行します。外部呼び出しを行うためのコールバックを提供します。引数と返品タイプはサービスタイプ固有のものであるため、詳細については、詳細については例を参照してください。結果の値はキャッシュされ、コールバックで提供されます。
void Reset ( )内部状態、すなわち、プロセスの呼び出しと内部キャッシュのために、つまり状態をリセットします(ServiceConconcurrencyオブジェクトが値を返す場合)。
bool IsExecuting ( )ServiceConcurrencyオブジェクトが現在特定のリクエストを実行しているかどうかを返します。
次の方法は、値を返すServiceConcurrencyオブジェクトでのみ使用できます。
void ResetCache ( )内部キャッシュをリセットします。 reset()から呼び出されます。
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を投げます。
IEnumerator < KeyValuePair < TArg , TValue > > GetEnumerator ( )
IEnumerator IEnumerable . GetEnumerator ( )内部キャッシュの列挙者。 foreachおよびlinqステートメントでオブジェクトを使用できます。
void Dispose ( ) Bool IsCacheSharedがfalseである場合に備えて、内部キャッシュを処分します。共有キャッシュの場合、代わりにResetCache呼び出すだけです。
TValue Value ;returnsvalue <tvalue>のみ。これは単一のキャッシュオブジェクトです。
次のプロパティは、値を返すServiceConconcurrencyオブジェクトでのみ使用できます。
MemoryCacheEntryOptions CacheEntryOptions ;これらのオプションは、値がキャッシュされているときに内部的に使用されます。これらを編集することで、有効期限、キャッシュサイズなどを設定できます。詳細については、MemoryCacheoptionsを参照してください。
bool IsCacheShared ;ゲッターのみ。キャッシュが共有されているかどうかを示します。
execute()は、オプションの値コンバーターを受け入れます。これは、返品してキャッシュする前にフェッチされた値を変更できます。これは、値を返すServiceConcurrencyオブジェクトでのみ使用できます。
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 ( ) ;
}
}