2009-05-11 5 views
3

Die folgende Klasse löst ein Ereignis für jeden neu registrierten "dataKey" aus und löst ein Ereignis aus, wenn ein "dataKey" abgemeldet wird, und zählt dafür "dataKey ist null.Werttypen und Wörterbuchabruf

Diese Klasse soll threadsicher sein und ich versuche, dies so performant wie möglich zu machen.

Meine Frage ist; In der Deregister-Methode kann ich die zweite Suche irgendwie entfernen, wenn ich den Wert aktualisiere (die _data [dataKey] = currentCountValue;)?

Ich kann die Variable currentCountValue nicht einfach aktualisieren, da der Wert nur auf dem lokalen Stapel und nicht im Wörterbuch aktualisiert wird.

Oder können Sie irgendwelche Leistungsverbesserungen vorschlagen? Ich glaube nicht, dass ich die Sperre entfernen und CAS-Operationen (Interlocked-Methoden) verwenden kann, um die Zählung zu aktualisieren, da das Wörterbuch bei Verwendung dieser Art nicht sicher für Updates ist ... oder?

/Ich verwende C# 3.0.

Danke für Ihre Zeit.

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

Antwort

2

als Gedanke - wenn Sie die „Set“ über einen Indexer nicht haben wollen, zu tun, können Sie die Zähler aus auf eine Klasse bewegen konnten?

class CounterBox { 
    public int Count {get;set;} 
} 

Dann haben Sie eine Dictionary<string,CounterBox>. Sie können jetzt Count außerhalb des Wörterbuchs aktualisieren und nur Remove(dataKey) aufrufen, wenn .Count Null ist. Dies wird eine zusätzliche De-Referenz haben, aber Sie müssen nicht über den Indexer zuweisen.

Für was ist schneller: Sie müssten ein Profil erstellen.

Etwas wie:

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

Ihre Event-Handling Thread-sicher nicht.

// Execute this ... 
if (NewKeyEvent != null) 

// ... other threads remove all event handlers here ... 

// ... NullReferenceException here. 
    NewKeyEvent(this, null); 

So besser geht es wie folgt.

0

Sie sollten mit der Art und Weise, wie Sie das Ereignis auslösen, vorsichtig sein (und jemand, der Ihre Registrierung bereits erwähnte, ist nicht threadsicher).

Sie rufen die Ereignishandler im Schloss auf. Das ist nicht per se unsicher, aber Sie riskieren, Ihre Datenstruktur vollständig zu blockieren. Da Sie natürlich nicht kontrollieren können, was in den von Ihnen aufgerufenen Event-Handlern passiert, wird Ihr Wörterbuch gesperrt, bis der Handler zurückkehrt, falls der Event-Handler selbst lange blockiert oder blockiert.

In einer Sperre sollten Sie niemals Methoden aufrufen, über die Sie keine Kontrolle haben, und niemals Methoden aufrufen, deren Ausführungszeit nicht deterministisch ist (alles, was auf irgendeine Weise nicht auf den Speicher zugreift). Wenn Sie dies tun, sind Sie anfällig dafür, dass Ihre Sperre unbegrenzt blockiert wird, auch wenn Ihr Code threadsicher ist.

Also in der Referenzierung und Dereferenzierung, sollten Sie entweder eine Kopie Ihrer Aufrufliste und rufen Sie es außerhalb der Sperre, oder rufen Sie die Delegierten selbst außerhalb (mit dem Muster Daniel erwähnt).

Verwandte Themen