.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 ( ) ;
}
}