下面的类为每个注册的新“dataKey”引发一个事件,并在取消注册“dataKey”时引发一个事件,并计算“dataKey为零。
这个课程的目标是线程安全,我试图尽可能提高性能。
我的问题是;在Deregister方法中,我可以在更新值时以某种方式删除第二个查找(_data [dataKey] = currentCountValue;)?
我无法简单地更新currentCountValue变量,因为该值仅在本地堆栈上更新,而不是在Dictionary中更新。
或者你可以提出任何性能改进吗?我不认为我可以删除锁并使用CAS操作(互锁方法)来更新计数,因为当这样使用时,字典对于更新不是线程安全的......对吗?
/我正在使用c#3.0。
感谢您的时间。
public sealed class DataCounter
{
public event EventHandler NewKeyEvent;
public event EventHandler ZeroCountEvent;
private readonly Dictionary<string, int> _data = new Dictionary<string, int>();
public void Register(string dataKey)
{
lock (_data)
{
if (_data.ContainsKey(dataKey))
{
_data[dataKey]++;
}
else
{
_data.Add(dataKey, 1);
if (NewKeyEvent != null) NewKeyEvent(this, null);
}
}
}
public void Deregister(string dataKey)
{
lock (_data)
{
int currentCountValue;
if (_data.TryGetValue(dataKey, out currentCountValue))
{
if (currentCountValue > 0)
{
currentCountValue--;
_data[dataKey] = currentCountValue;
}
if (currentCountValue == 0)
{
if (ZeroCountEvent != null) ZeroCountEvent(this, null);
}
}
}
}
}
答案 0 :(得分:2)
作为一个想法 - 如果你不想通过索引器进行“设置”,你可以将计数器移到一个类上吗?
class CounterBox {
public int Count {get;set;}
}
然后有一个Dictionary<string,CounterBox>
。您现在可以在字典外更新Count
,仅在Remove(dataKey)
为零时调用.Count
。这将有额外的去引用,但您不必通过索引器进行分配。
哪个更快:你需要个人资料。
类似的东西:
public sealed class DataCounter
{
private class CounterBox
{
public int Count { get; set; }
}
public event EventHandler NewKeyEvent;
public event EventHandler ZeroCountEvent;
private readonly Dictionary<string, CounterBox> _data
= new Dictionary<string, CounterBox>();
public void Register(string dataKey)
{
lock (_data)
{
CounterBox box;
if (_data.TryGetValue(dataKey, out box))
{
box.Count++;
}
else
{
_data.Add(dataKey, new CounterBox { Count = 1 });
EventHandler handler = NewKeyEvent;
if (handler != null) handler(this, EventArgs.Empty);
}
}
}
public void Deregister(string dataKey)
{
lock (_data)
{
CounterBox box;
if (_data.TryGetValue(dataKey, out box))
{
if (box.Count > 0)
{
box.Count--;
}
if (box.Count == 0)
{
EventHandler handler = ZeroCountEvent;
if (handler != null) handler(this, EventArgs.Empty);
_data.Remove(dataKey);
}
}
}
}
}
答案 1 :(得分:0)
您的事件处理不是线程安全的。
// Execute this ...
if (NewKeyEvent != null)
// ... other threads remove all event handlers here ...
// ... NullReferenceException here.
NewKeyEvent(this, null);
最好这样做。
EventHandler newKeyEvent = this.newKeyEvent;
if (newKeyEvent != null)
{
newKeyEvent(this, null);
}
答案 2 :(得分:0)
你应该小心你举起活动的方式(有人已经提到你的注册不是线程安全的。)
您正在调用锁内的事件处理程序。这本身并不是线程不安全,但您可能会完全停止数据结构。因为你显然无法控制你正在调用的事件处理程序中发生的事情,如果事件处理程序本身阻塞很长时间,或者冻结,你的字典将被锁定,直到处理程序返回。
在锁定中,你不应该调用你无法控制的方法,也不要调用任何执行时间不确定的方法(任何不以某种方式访问内存的方法)。如果这样做,即使你的代码是线程安全的,你也很容易无限期地锁定你的锁。
因此,在引用和解除引用时,您应该拥有调用列表的副本并将其称为锁定的OUTSIDE,或者在外部调用委托自身(使用Daniel提到的模式)。