Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 联锁。字典值的增量_C#_.net_Multithreading_Web Services - Fatal编程技术网

C# 联锁。字典值的增量

C# 联锁。字典值的增量,c#,.net,multithreading,web-services,C#,.net,Multithreading,Web Services,我试图使用字典记录web服务上每个API路径的当前请求计数,并增加和减少当前计数,我认为使用Interlocked.Increment()是一个好方法,因为它会增加计数,同时读取计数 然而,下面的代码给出了一个错误,说ref参数没有被分类为变量,我猜这是因为dict[key]不是变量 var dict = new Dictionary<string, int>(); dict.Add("a", 1); int i = Interlocked.Increment(ref dict["a

我试图使用字典记录web服务上每个API路径的当前请求计数,并增加和减少当前计数,我认为使用
Interlocked.Increment()
是一个好方法,因为它会增加计数,同时读取计数

然而,下面的代码给出了一个错误,说ref参数没有被分类为变量,我猜这是因为
dict[key]
不是变量

var dict = new Dictionary<string, int>();
dict.Add("a", 1);
int i = Interlocked.Increment(ref dict["a"]);

使用
联锁
的主要原因是性能。如果您没有性能问题,那么如果您只需使用
,您的代码将被更多的人理解,并且更易于编写和阅读

如果您必须使用
联锁
,那么就不能用您尝试过的方式使用字典<代码>互锁操作是原子操作,通常在CPU级别,它们需要在内存中的一个固定位置进行操作。字典上的属性访问器不提供此功能

如果您仍然想使用字典,有两种方法会浮现在您的脑海中:

将计数存储在数组中 您将把您的计数值存储在一个数组中。每个单元都固定在内存中,因此可以由
联锁的
使用。您的字典将在存储计数的数组中存储索引,而不是存储计数。当然,您可以将所有这些都写入一个类中,以便将这些实现细节隐藏在其中

在字典中存储“计数对象” 字典中的每一项都是一个包含计数的类的实例。在类的内部,将有一个
私有int
,可由
联锁
使用。您的类将提供
递增
递减
方法,以及一个只读的
计数
属性,以便以类似的方式使用它

编辑

使用信号灯
实际上,您可以研究的另一件事是使用
信号量。他们几乎是。通过让字典中的每个单元格都是一个信号量而不是一个计数,您可以以线程安全的方式实现一个非常相似的事情。您将执行
dictionary[key].WaitOne(0)
,如果成功,将返回true,否则返回false。如果返回true,则该信号量的计数已经增加,您只需稍后再次调用
字典[hey].Release()

在字典中存储可变堆对象:

ConcurrentDictionary<..., StrongBox<int>> dict = ...;
Interlocked.Increment(ref dict[...].Value);
ConcurrentDictionary dict=。。。;
联锁增量(参考指令[…]值);

StrongBox.Value
是一个可变字段。

实际上更简单。特别是如果您不确定是否创建了密钥

如果存在,则下面的将值增加1;如果不存在,则使用默认值1创建该值。Concurrents命名空间包含构建线程安全对象所需的几乎所有内容。我通常不喜欢使用锁,因为它序列化了对对象的访问(如果我们要以串行方式执行,为什么要执行多线程?)

ConcurrentDictionary数据存储=新建ConcurrentDictionary();
公共整数增量(字符串键)
{
返回dataStore.AddOrUpdate(键,1,(k,v)=>Interlocked.Increment(参考v));
}

您想做什么?即使这段代码有效,也不能保证线程安全地修改存储值。它只能保证返回副本的线程安全。也许您正在寻找ConcurrentDictionary或其中一个不可变类?@PanagiotisKanavos,为什么这不能保证对字典中的值进行线程安全的修改..我希望Interlocated.Increment(ref something)将确保不同的线程可以安全地修改和读取某个值。我将编辑该问题以提供更多细节。谢谢。尝试使用
lock
语句,而不是简单地使用InterlockedOK。字典是通过属性get完成的,所以不能对其值执行互锁操作。但是,您可以对阵列单元执行联锁操作—它们是直接访问的固定内存位置。因此,您将设置一个大数组,然后您的字典,而不是返回计数本身,将返回一个索引到该数组中,然后计数将存储在数组中。然后可以在阵列单元上执行联锁操作。当然,您可以将其封装在一个类中,这样使用该类看起来就像使用字典一样。另一种方法是字典中的每个项都是一个“Count”对象。您可以调用这些对象上的方法来递增、递减和获取当前值。在内部,这些对象使用Interlocked递增和递减它们的私有
int
,使用属性访问器以只读方式访问。@user8549339请比“error”更具体一些。我将答案更改为使用ConcurrentDictionary,以便您可以安全地添加到其中。这确实是一个bug。它实际上与我自己的代码有关。它确实可以工作:)虽然对于我当前的设置,我不需要ConcurrentDictionary。在
updateValueFactory
委托中使用
Interlocked.Increment
是多余的<代码>(k,v)=>v+1就可以了。
v
变量是本地变量,其他线程不能同时访问。该方法通过丢弃返回值并重新调用委托来伪造原子性,以防另一个线程更新特定密钥的速度更快。@TheodorZoulias,没错,并发字典实现中有一个锁。这对许多不太了解ConcurrentDictionary的人来说是一种误导,因此我建议删除它。这是必需的,因为updateValueFactory上没有锁。但是,请参阅
,在锁外部调用updateValueFactory委托以避免可能出现的问题
ConcurrentDictionary<..., StrongBox<int>> dict = ...;
Interlocked.Increment(ref dict[...].Value);
ConcurrentDictionary<string, int> dataStore = new ConcurrentDictionary<string, int>();

public int Increment(string key)
{
    return dataStore.AddOrUpdate(key, 1, (k, v) => Interlocked.Increment(ref v));
}