God*_*ent 5 .net c# multithreading web-services
我正在尝试使用Dictionary来记录Web服务上每个API路径的当前请求数,并增加和减少当前计数我认为一个好方法将使用,Interlocked.Increment()因为它增加了计数并在同一时间读取计数时间.
但是下面的代码给出了一个错误,说ref参数不被归类为变量,我猜是因为dict[key]它不是变量?
var dict = new Dictionary<string, int>();
dict.Add("a", 1);
int i = Interlocked.Increment(ref dict["a"]);
Run Code Online (Sandbox Code Playgroud)
我知道Interlocked.Increment()不能应用于属性,但不会想到通过密钥访问字典会有同样的问题.
最好的方法是什么?
编辑:这里有一些关于此的更多细节.
我正在尝试编写一些代码来限制Web服务的每个API路径上的API调用的数量,因此我有两个字典,策略字典指定每个API路径上允许的并发调用方数和第二个计数字典记录每个API路径上当前有多少呼叫者处于活动状态.
在Web服务执行任何传入请求之前,它将检查上述两个字典,以确定请求是否应该继续,或者只是立即返回HTTP 429(Too Many Requests)响应.
以下是代码的摘录,它首先检查是否存在匹配的策略,然后如果是,则检查是否违反了最大允许请求.
public override async Task Invoke(IOwinContext context)
{
var url = context.Request.Path.ToString();
var policy = _policies.FirstOrDefault(x => x.EndpointPath == url);
if (policy != null)
{
try
{
if (Interlocked.Increment(ref _currentRequests[policy]) > policy.MaxConcurrentConnection)
{
context.Response.StatusCode = 429;
var message = string.Format(
"Max API concurrent calls quota exceeded, please try again later. Maximum admitted: {0}",
policy.MaxConcurrentConnection);
context.Response.Write(message);
context.Response.ReasonPhrase = "Too Many Requests";
}
else
{
await Next.Invoke(context);
}
}
finally
{
Interlocked.Decrement(ref _currentRequests[policy]);
}
}
else
{
await Next.Invoke(context);
}
}
Run Code Online (Sandbox Code Playgroud)
将可变堆对象存储在字典中:
ConcurrentDictionary<..., StrongBox<int>> dict = ...;
Interlocked.Increment(ref dict[...].Value);
Run Code Online (Sandbox Code Playgroud)
StrongBox.Value 是一个可变的领域.
小智 7
这实际上要容易得多。特别是如果您不确定密钥是否已创建。
下面,如果存在,则将值加一,如果不存在,则使用默认值 1 创建它。Concurrents 命名空间包含构建线程安全对象所需的几乎所有内容。我通常不喜欢使用锁,因为它会序列化对对象的访问(如果我们要串行执行,为什么要执行多线程?)
ConcurrentDictionary<string, int> dataStore = new ConcurrentDictionary<string, int>();
public int Increment(string key)
{
return dataStore.AddOrUpdate(key, 1, (k, v) => Interlocked.Increment(ref v));
}
Run Code Online (Sandbox Code Playgroud)
您使用的主要原因Interlocked是性能.如果您没有性能问题,那么如果您只是使用一个,那么您的代码对于更多人来说是可以理解的,并且更容易编写和阅读lock.
如果你绝对必须使用Interlocked,那么就不能按照你想要的方式用字典来做.Interlocked操作是原子的,通常在CPU级别,并且它们需要在内存中的固定位置来操作.字典上的属性访问器不提供此功能.
如果您仍想使用词典,可以考虑两种方法:
您将计数值存储在数组中.每个单元都固定在内存中,因此可以使用Interlocked.字典而不是存储计数会将索引存储在存储计数的数组中.当然,您可以将这一切写入一个类,以便隐藏这些实现细节.
字典中的每个项目都是保存计数的类的实例.在课堂内部private int,可以使用一个Interlocked.您的类将提供Increment和Decrement方法以及只读Count属性,以允许以类似的方式使用它.
编辑
实际上,你可以研究的另一件事是使用a Semaphore.它们几乎就是为此而设计的.通过使Dictionary中的每个单元格成为一个Semaphore而不是一个计数,您可以以线程安全的方式实现非常相似的事物.你会这样做dictionary[key].WaitOne(0),如果成功则返回true,否则返回false.如果它确实返回true,那么信号量的计数已经增加,你Dictionary[hey].Release()稍后再调用.