当前位置:  开发笔记 > 编程语言 > 正文

Lazy <T>无异常缓存

如何解决《Lazy<T>无异常缓存》经验,为你挑选了1个好方法。



1> piotrwest..:

不幸的是,这是错误的解决 请忽略它并使用tsul答案.只有当你想调试它并发现bug时才离开它.

这是使用tsul SimpleLazy的工作解决方案(与工厂并发缓存):https://dotnetfiddle.net/Y2GP2z


我最终得到了以下解决方案:包装Lazy以模仿与Lazy相同的功能但没有异常缓存.

这是LazyWithoutExceptionsCaching类:

public class LazyWithoutExceptionCaching
{
    private readonly Func _valueFactory;
    private Lazy _lazy;

    public LazyWithoutExceptionCaching(Func valueFactory)
    {
        _valueFactory = valueFactory;
        _lazy = new Lazy(valueFactory);
    }

    public T Value
    {
        get
        {
            try
            {
                return _lazy.Value;
            }
            catch (Exception)
            {
                _lazy = new Lazy(_valueFactory);
                throw;
            }
        }
    }
}

完整的工作示例(在这里FIDDLE):

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using System.Net;

namespace Rextester
{
    public class Program
    {
        public class LazyWithoutExceptionCaching
        {
            private readonly Func _valueFactory;
            private Lazy _lazy;

            public LazyWithoutExceptionCaching(Func valueFactory)
            {
                _valueFactory = valueFactory;
                _lazy = new Lazy(valueFactory);
            }

            public T Value
            {
                get
                {
                    try
                    {
                        return _lazy.Value;
                    }
                    catch (Exception)
                    {
                        _lazy = new Lazy(_valueFactory);
                        throw;
                    }
                }
            }
        }

        public class LightsaberProvider
        {
            private static int _firstTime = 1;

            public LightsaberProvider()
            {
                Console.WriteLine("LightsaberProvider ctor");
            }

            public string GetFor(string jedi)
            {
                Console.WriteLine("LightsaberProvider.GetFor jedi: {0}", jedi);

                Thread.Sleep(TimeSpan.FromSeconds(1));
                if (jedi == "2" && 1 == Interlocked.Exchange(ref _firstTime, 0))
                {
                    throw new Exception("Dark side happened...");
                }

                Thread.Sleep(TimeSpan.FromSeconds(1));
                return string.Format("Lightsaver for: {0}", jedi);
            }
        }

        public class LightsabersCache
        {
            private readonly LightsaberProvider _lightsaberProvider;
            private readonly ConcurrentDictionary> _producedLightsabers;

            public LightsabersCache(LightsaberProvider lightsaberProvider)
            {
                _lightsaberProvider = lightsaberProvider;
                _producedLightsabers = new ConcurrentDictionary>();
            }

            public string GetLightsaber(string jedi)
            {
                LazyWithoutExceptionCaching result;
                if (!_producedLightsabers.TryGetValue(jedi, out result))
                {
                    result = _producedLightsabers.GetOrAdd(jedi, key => new LazyWithoutExceptionCaching(() =>
                    {
                        Console.WriteLine("Lazy Enter");
                        var light = _lightsaberProvider.GetFor(jedi);
                        Console.WriteLine("Lightsaber produced");
                        return light;
                    }));
                }
                return result.Value;
            }
        }

        public static void Main(string[] args)
        {
            Test();
            Console.WriteLine("Maximum 1 'Dark side happened...' strings on the console there should be. No more, no less.");
            Console.WriteLine("Maximum 5 lightsabers produced should be. No more, no less.");
        }

        private static void Test()
        {
            var cache = new LightsabersCache(new LightsaberProvider());

            Parallel.For(0, 15, t =>
            {
                for (int i = 0; i < 10; i++)
                {
                    try
                    {
                        var result = cache.GetLightsaber((t % 5).ToString());
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                    Thread.Sleep(25);
                }
            });
        }
    }
}


不幸的是,这不是线程安全的,因为两个线程可以同时写入_lazy.你可能也需要锁定它.
是的,但锁定应该在try/catch周围,而不是在其中.如果两个线程同时尝试,则它们可以获得两个不同的值,因此它不是线程安全的.
推荐阅读
我我檬檬我我186
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有