Типы значений и словарь поиска - PullRequest
3 голосов
/ 11 мая 2009

Класс ниже вызывает событие для каждого нового зарегистрированного «dataKey» и вызывает событие, когда «dataKey» отменяется и счетчик для этого «dataKey» равен нулю.

Этот класс стремится быть поточно-ориентированным, и я пытаюсь сделать его как можно более производительным.

Мой вопрос; в методе Deregister я могу каким-то образом удалить 2-й поиск при обновлении значения (_data [dataKey] = currentCountValue;)?

Я не могу просто обновить переменную currentCountValue, так как значение обновляется только в локальном стеке, а не в Словаре.

Или вы можете предложить какие-либо улучшения производительности? Я не думаю, что смогу снять блокировку и использовать операции 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);
                }
            }
        }
    }
}

Ответы [ 3 ]

2 голосов
/ 11 мая 2009

В качестве мысли - если вы не хотите выполнять "набор" через индексатор, вы можете переместить счетчик в класс?

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);
                }
            }
        }
    }
}
0 голосов
/ 11 мая 2009

Вы должны быть осторожны с тем, как вы поднимаете событие (и кто-то уже упоминал, что ваша регистрация небезопасна).

Вы вызываете обработчики событий внутри замка. Это не небезопасно как таковое, но вы рискуете полностью остановить свою структуру данных. Поскольку вы, очевидно, не можете контролировать то, что происходит в вызываемых вами обработчиках событий, если обработчик событий сам блокируется на долгое время или зависает, ваш словарь блокируется до тех пор, пока обработчик не вернется.

В блокировке вы НЕ ДОЛЖНЫ вызывать методы, над которыми у вас нет контроля, и никогда не вызывать ни один метод, время выполнения которого не является детерминированным (что-либо, что так или иначе не обращается к памяти). Если вы это сделаете, вы уязвимы, так как ваша блокировка блокируется на неопределенный срок, даже если ваш код является поточно-ориентированным.

Таким образом, в ссылках и разыменованиях вы должны либо иметь копию своего списка вызовов и называть ее ВНЕ замка, либо вызывать самого делегата снаружи (используя шаблон, упомянутый Дэниелом).

0 голосов
/ 11 мая 2009

Ваша обработка событий не является поточно-ориентированной.

// 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);
 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...