.NET的並發和狀態庫,在網絡調用中閃耀。
Nuget軟件包可在https://www.nuget.org/packages/serviceconcurrency/
用於處理並發和狀態的庫,主要用於呼籲Web服務的代碼。
首先,它可以防止不必要的電話發生。當匹配爭論的呼叫已經在飛行中時,並發呼叫者停放,並在原始請求完成時恢復。如果該論點是一個集合,則已經剝離了飛行的實體。
其次,它緩存了返回請求的價值狀態。當存在任何給定請求的緩存值時,將返回它而不是撥打呼叫。可以隨時訪問此值,以防止您的代碼中需要其他備份字段。如果該論點是一個集合,則將緩存的實體從論點中剝離。
該庫使用ImeMoryCache進行數據內部緩存。
這是ServiceConcorrency可以為您處理的一些事情。在本文檔的後面部分中可以找到更多的深入示例。
getSessionToken()將在第一次通話後返回緩存的價值。如果getSessionToken()在尚未有一個值中的值時同時調用,則只會撥打一個呼叫()呼叫,並且其他並發的呼叫將產生,直到有一個值可用為止。
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
) ;
} 該庫中有四個類:
Noargnovalue
一次僅允許一次主動呼叫來防止並發呼叫,同時通話將等待主動呼叫完成。
returnsvalue <tvalue>
只會撥打一個呼叫,隨後的呼叫將從緩存中獲取值。還可以通過一次只允許一個主動調用來防止任何並發呼叫發生,同時呼叫將等待主動呼叫完成。
Takearg <targ>
通過每次參數一次僅允許一個活動呼叫,在共享同一參數時防止並發呼叫,並發通話將等待主動調用完成。
Takeargreturnsvalue <targ,tvalue>
對於給定的參數,只會撥打一個呼叫,然後隨後的調用將從緩存中獲取值。還可以通過每次參數一次允許一個主動呼叫來防止任何並發的呼叫發生,同時呼叫將等待主動調用完成。
takeenumerationarg <targ>
並發呼叫僅對參數集合中的給定參數執行一次。
參數集合已剝奪了已經在飛行中的操作的值。
因此,與[“ A”,“ B”,“ C”]和[B',“ C”,“ D”]同時致電,將導致一個呼叫[A“ A”,“ B”,“ C”],一個帶有[D']的呼叫。
takeenumerationargreturnsvalue <targ,tvalue>
第一個呼叫和任何並發呼叫將僅對參數集合中的給定參數執行一次。隨後的呼叫將從緩存中獲取值。
參數收集被剝奪了存在緩存值或已在飛行中的操作的值。
因此,與[“ A”,“ B”,“ C”]和[B',“ C”,“ D”]同時致電,將導致一個呼叫[A“ A”,“ B”,“ C”],一個帶有[D']的呼叫。下次調用“ A”,“ B”,“ C”或“ D”時,它將從集合中剝離,並將其值從緩存中獲取。
所有ServiceConcorrency對像都有一個無參數構造函數,在這種情況下,將創建內部ImeMoryCache。
返回值的所有ServiceConconrency對像都有一個接受ImeMoryCache對象的構造函數,以防您要與所有ServiceConconcorrency對象共享緩存。如果這樣做,請確保在條目上使用獨特的鍵。
T Execute ( .. . )執行特定請求。您提供了進行外部通話的回調。有關更多信息,請參見示例,因為參數和返回類型是特定於ServiceConcorrency類型。最終的值是緩存的,並在您的回調中提供給您。
void Reset ( )重置內部狀態,即在過程中調用的狀態和內部緩存(如果ServiceConcorrency對象返回值)。
bool IsExecuting ( )返回ServiceConcorrency對象當前是否正在執行特定請求。
以下方法僅在返回值的ServiceConconrency對像中可用。
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扔到getter中。
IEnumerator < KeyValuePair < TArg , TValue > > GetEnumerator ( )
IEnumerator IEnumerable . GetEnumerator ( )內部緩存的枚舉器。允許您在foreach和linq語句中使用對象。
void Dispose ( )如果bool IsCacheShared是錯誤的,則處置內部緩存。如果是共享緩存,則只會調用ResetCache 。
TValue Value ;僅作為返回value <tvalue>。這是單個緩存對象。
以下屬性僅在返回值的ServiceConconrency對像中可用。
MemoryCacheEntryOptions CacheEntryOptions ;當值緩存時,這些選項在內部使用。編輯這些允許您設置到期,緩存大小等。有關更多詳細信息,請參見MemoryCacheoptions。
bool IsCacheShared ;只有Getter。表示緩存是否共享。
Execute()接受可選的值轉換器,該轉換器可以在返回和緩存之前修改獲取的值。這僅在返回值的ServiceConcorrency對像中可用。
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 } " ;
) ;
}ServiceConcorrency對像還接受一個附加參數,該參數聲明內部請求的值類型。指定此功能時,值轉換器還將在源類型和目標類型之間轉換。如果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 ( ) ;
}
}