Object cache for C#

前端 未结 9 1401
面向向阳花
面向向阳花 2020-12-07 23:06

I\'m doing a document viewer for some document format. To make it easier, let\'s say this is a PDF viewer, a Desktop application. One requirement for the s

相关标签:
9条回答
  • 2020-12-08 00:00

    The .NET Framework has always had the ability to keep weak references to objects.

    Basically, weak references are references to objects that the runtime considers "unimportant" and that may be removed by a garbage collection run at any point in time. This can be used, for example, to cache things, but you'd have no control over what gets colected and what not.

    On the other hand, it's very simple to use and it may just be what you need.

    Dave

    0 讨论(0)
  • 2020-12-08 00:01

    I wrote an LRU Cache and some test cases, feel free to use it.

    You can read through the source on my blog.

    For the lazy (here it is minus the test cases):

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace LRUCache {
        public class IndexedLinkedList<T> {
    
            LinkedList<T> data = new LinkedList<T>();
            Dictionary<T, LinkedListNode<T>> index = new Dictionary<T, LinkedListNode<T>>();
    
            public void Add(T value) {
                index[value] = data.AddLast(value);
            }
    
            public void RemoveFirst() {
                index.Remove(data.First.Value);
                data.RemoveFirst();
            }
    
            public void Remove(T value) {
                LinkedListNode<T> node;
                if (index.TryGetValue(value, out node)) {
                    data.Remove(node);
                    index.Remove(value);
                }
            }
    
            public int Count {
                get {
                    return data.Count;
                }
            }
    
            public void Clear() {
                data.Clear();
                index.Clear();
            }
    
            public T First {
                get {
                    return data.First.Value;
                }
            }
        }
    }
    

    LRUCache

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace LRUCache {
        public class LRUCache<TKey, TValue> : IDictionary<TKey, TValue> {
    
            object sync = new object();
            Dictionary<TKey, TValue> data;
            IndexedLinkedList<TKey> lruList = new IndexedLinkedList<TKey>();
            ICollection<KeyValuePair<TKey, TValue>> dataAsCollection;
            int capacity;
    
            public LRUCache(int capacity) {
    
                if (capacity <= 0) {
                    throw new ArgumentException("capacity should always be bigger than 0");
                }
    
                data = new Dictionary<TKey, TValue>(capacity);
                dataAsCollection = data;
                this.capacity = capacity;
            }
    
            public void Add(TKey key, TValue value) {
                if (!ContainsKey(key)) {
                    this[key] = value;
                } else {
                    throw new ArgumentException("An attempt was made to insert a duplicate key in the cache.");
                }
            }
    
            public bool ContainsKey(TKey key) {
                return data.ContainsKey(key);
            }
    
            public ICollection<TKey> Keys {
                get {
                    return data.Keys;
                }
            }
    
            public bool Remove(TKey key) {
                bool existed = data.Remove(key);
                lruList.Remove(key);
                return existed;
            }
    
            public bool TryGetValue(TKey key, out TValue value) {
                return data.TryGetValue(key, out value);
            }
    
            public ICollection<TValue> Values {
                get { return data.Values; }
            }
    
            public TValue this[TKey key] {
                get {
                    var value = data[key];
                    lruList.Remove(key);
                    lruList.Add(key);
                    return value;
                }
                set {
                    data[key] = value;
                    lruList.Remove(key);
                    lruList.Add(key);
    
                    if (data.Count > capacity) {
                        data.Remove(lruList.First);
                        lruList.RemoveFirst();
                    }
                }
            }
    
            public void Add(KeyValuePair<TKey, TValue> item) {
                Add(item.Key, item.Value);
            }
    
            public void Clear() {
                data.Clear();
                lruList.Clear();
            }
    
            public bool Contains(KeyValuePair<TKey, TValue> item) {
                return dataAsCollection.Contains(item);
            }
    
            public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
                dataAsCollection.CopyTo(array, arrayIndex);
            }
    
            public int Count {
                get { return data.Count; }
            }
    
            public bool IsReadOnly {
                get { return false; }
            }
    
            public bool Remove(KeyValuePair<TKey, TValue> item) {
    
                bool removed = dataAsCollection.Remove(item);
                if (removed) {
                    lruList.Remove(item.Key);
                }
                return removed;
            }
    
    
            public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {
                return dataAsCollection.GetEnumerator();
            }
    
    
            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
                return ((System.Collections.IEnumerable)data).GetEnumerator();
            }
    
        }
    }
    
    0 讨论(0)
  • 2020-12-08 00:02

    Caching application block and ASP.NET cache are both options however, although they do LRU, the only kind of disk utilization that happens is by memory paging. I think there are ways you can optimize this that are more specific to your goal to get a better output. Here are some thoughts:

    • Since it's an ASP.NET app, why not generate the images, write them to disk, and when the browser requests the next page have IIS serve it up. That keeps your ASP.NET worker process lower while letting IIS do what it's good at.
    • Use statistics about how a user interacts. LRU as implemented in the ASP.NET cache will typically apply to the individual image files - not much use for this scenario by the sounds of it. Instead, maybe some logic like: "If the user has scrolled X pages in the last Y seconds, then general the next X*Y images". Users scrolling quickly will get more pages generated; users reading slowly will need less cached.
    • Have IIS serve images from disk, and use a custom HTTP handler for images you really want to control the caching of.
    • Have the browser request the files ahead of time too, and rely on browser caching.
    • Once an image has been served to the client, is it fair to say it can pretty much be removed from the cache? That could reduce the footprint substantially.

    I'd certainly avoid using a plain hash table though.

    0 讨论(0)
提交回复
热议问题