正在使用或不使用存储实例的快速线程安全缓存

时间:2017-12-17 09:11:36

标签: c# caching .net-4.0

我需要一个线程安全缓存来存储一次性类的实例。

  • 它将与.NET 4.0一起使用
  • 缓存应该知道是否使用了存储的实例。
  • 当想要从缓存中获取实例时,它应该查看存储的可用实例并给出一个实例;如果没有,请创建一个新实例并存储它。
  • 如果缓存在一段时间内未被使用,缓存应该处理存储的,未使用的insances并清除它们。

这是我写的解决方案:

private class cache<T> where T : IDisposable
{
    Func<T> _createFunc;
    long livingTicks;
    int livingMillisecs;
    public cache(Func<T> createFunc, int livingTimeInSec)
    {
        this.livingTicks = livingTimeInSec * 10000000;
        this.livingMillisecs = livingTimeInSec * 1000;
        this._createFunc = createFunc;
    }
    Stack<T> st = new Stack<T>();
    public IDisposable BeginUseBlock(out T item)
    {
        this.actionOccured();
        if (st.Count == 0)
            item = _createFunc();
        else
            lock (st)
                if (st.Count == 0)
                    item = _createFunc();
                else
                    item = st.Pop();
        return new blockDisposer(this, item);
    }



    long _lastTicks;
    bool _called;
    private void actionOccured()
    {
        if (!_called)
            lock (st)
                if (!_called)
                {
                    _called = true;
                    System.Threading.Timer timer = null;
                    timer = new System.Threading.Timer((obj) =>
                    {
                        if ((DateTime.UtcNow.Ticks - _lastTicks) > livingTicks)
                        {
                            timer.Dispose();
                            this.free();
                        }
                    },
                    null, livingMillisecs, livingMillisecs);
                }

        _lastTicks = DateTime.UtcNow.Ticks;
    }
    private void free()
    {
        lock (st)
        {
            while (st.Count > 0)
                st.Pop().Dispose();
            _called = false;
        }
    }

    private class blockDisposer : IDisposable
    {
        T _item;
        cache<T> _c;
        public blockDisposer(cache<T> c, T item)
        {
            this._c = c;
            this._item = item;
        }
        public void Dispose()
        {
            this._c.actionOccured();
            lock (this._c.st)
                this._c.st.Push(_item);
        }
    }
}

这是一个示例用途:

class MyClass:IDisposable
{
    public MyClass()
    {
        //expensive work
    }
    public void Dispose()
    {
        //free
    }
    public void DoSomething(int i)
    {
    }
}
private static Lazy<cache<MyClass>> myCache = new Lazy<cache<MyClass>>(() => new cache<MyClass>(() => new MyClass(), 60), true);//free 60sec. after last call
private static void test()
{
    Parallel.For(0, 100000, (i) =>
    {
        MyClass cl;
        using (myCache.Value.BeginUseBlock(out cl))
            cl.DoSomething(i);
    });
}

我的问题:

  • 有更快的方法吗? (我已经搜索了MemoryCache的例子,但是我不知道如何根据我的要求使用它。它需要一个关键的检查。我认为Stack.Pop会比关键搜索更快;而且我的问题也是如此,表现非常重要。)
  • 为了在一段时间后处理实例(示例代码为60秒),我必须使用Timer。我只需要一个延迟的函数调用,它会在缓存发生的每个动作上重新延迟。有没有办法在不使用计时器的情况下做到这一点?

修改 我试过@ mjwills的评论。这个表现更好:

    ConcurrentStack<T> st = new ConcurrentStack<T>();
    public IDisposable BeginUseBlock(out T item)
    {
        this.actionOccured();
        if (!st.TryPop(out item))
            item = _createFunc();
        return new blockDisposer(this, item);
    }

EDIT2: 在我的cas中它不是必需的,但如果我们需要控制堆栈的大小并处理未使用的对象,使用一个单独的计数器,它将使用Interlocked.Increment递增递减(@mjwills)

0 个答案:

没有答案