IMMUTABLE COLLECTIONS(1)

简介:
Immutable Collections1

/玄魂

前言

 .NET4.0开始,到现在的4.5,我们可以感受得到微软在并行、多线程、异步编程上带给开发人员的惊喜。在多线程开发中,无可避免的涉及多个线程共享对象问题,Immutable Object(不可变对象)在保证线程安全方面的重要性被凸显出来。简单不可变对象,比如单例,我们可以很轻松的创建并维护,一些复杂对象,对象引用或者集合对象的场景 ,创建和维护不可变对象变得困难了很多。微软在这方面也做了很多努力,目前看最令我欣喜的就是Immutable Collections了。如果您了解函数式编程,那么对此肯定不会陌生。

当然除了线程安全,不可变集合还有其他的应用场景,本文也会有所涉及。

笔者最近研读了几篇MSDN Blog中关于Immutable Collections的英文博文(在文后会给出链接)。我看到的博客中的代码和我下载的版本有些出入,我根据自己的理解重新整理,改编成此文,水平有限,欢迎讨论。

1.1  IMMUTABILITY OBJECT简单分类

真正的不可变对象

这类对象只能在编译时赋值,在C#const类型的变量属于这个类型。

一次初始化对象

运行时初始化一次,之后再也不会被改变。典型的单例对象就属于这一类。

浅度不变和深度不变

C#为例,对象本身是Static ReadOnly类型,但是这不能保证该对象内部成员是线程安全的,这类对象具有浅度不变性,如果能保证对象本身、对象内部任何成员或者嵌套成员都具有不变性则该对象具有深度不变性。

显然,具有深度不变性的对象是理想的线程安全模型。

1.2 安装和使用

不要误会安装的含义,这里是指从Nuget安装提供不可变集合功能的Dll

运行环境:vs2012.NET 4.5

PM> Install-Package Microsoft.Bcl.Immutable -pre

您正在从 Microsoft 下载 Microsoft.Bcl.Immutable,有关此程序包的许可协议在http://go.microsoft.com/fwlink/?LinkID=272980&clcid=0x409 上提供。请检查此程序包是否有其他依赖项,这些依赖项可能带有各自的许可协议。您若使用程序包及依赖项,即构成您接受其许可协议。如果您不接受这些许可协议,请从您的设备中删除相关组件。

已成功安装“Microsoft.Bcl.Immutable 1.0.8-beta”

已成功将“Microsoft.Bcl.Immutable 1.0.8-beta”添加到。。

这个Preview版本的安装包包含了如下不可变类型:

·             ImmutableStack<T>

·             ImmutableQueue<T>

·             ImmutableList<T>

·             ImmutableHashSet<T>

·             ImmutableSortedSet<T>

·             ImmutableDictionary<K, V>

·             ImmutableSortedDictionary<K, V>

每种类型都继承自相应的接口,从而保证之后不可变类型的可扩展性。

先以ImmutableList<T>为例,开始我们的不可变集合之旅。

class Program

    {

        static void Main(string[] args)

        {

            ImmutableList<string> emptyBusket = ImmutableList.Create<string>();

        }

    }

注意上面的代码,我们没有使用构造函数来初始化ImmutableList<string>集合,而是使用名为ImmutableListCreate方法,该方法返回一个空的不可变集合。使用空集合在某些情况下可以避免内存浪费。

CREATE方法有7个重载,可以传入初始化数据和比较器。

下面我们尝试向这个集合中添加一些数据。

 class Program

    {

        static void Main(string[] args)

        {

            ImmutableList<string> emptyBusket = ImmutableList.Create<string>();

            var fruitBasket = emptyBusket.Add("apple");

        }

    }

我想您已经看到Immutable Collections和传统集合的一个区别 了,Add方法创建了一个新的集合。这里我们也可以使用AddRange方法批量添加数据创建新的实例。

1.3 BUILDERS

有时,我们可能更需要对一个集合多次修改才能到达要求。从上面的示例我们知道,每次修改都会创建新的集合,这就意味着要开辟新的内存,并且存在数据拷贝。程序本身的执行效率会下降同时GC压力会增大。

其实同样的问题再String类型上也存在,反复的修改字符串值存在同样的问题,.NETStringBuilder用来解决这个问题。类似的IMMUTABLE COLLECTIONS也提供了Builder类型。同时我们具有了在迭代的同时修改集合的能力!

下面我们来看看Builder的基本应用:

    static void Main(string[] args)

        {

            ImmutableList<string> fruitBusket = ImmutableList.Create<string>("apple","orange","pear");

          

            var builder = fruitBusket.ToBuilder();

            foreach (var fruit in fruitBusket)

            {

                if (fruit == "pear")

                {

                    builder.Remove(fruit);

                }

            }

            builder.Add("ananas");

            fruitBusket = builder.ToImmutable();

            foreach (var f in fruitBusket)

            {

                Console.WriteLine(f);

            }

            Console.Read();

        }

在上面的代码中,使用ToBuilder方法获取Builder对象,在最后使用To ToImmutable方法返回IMMUTABLE COLLECTION。这里需要注意的是ToBuilder方法并没有拷贝资源给新Builder对象,Builder的所有操作都和集合共享内存。也许您要怀疑,既然是共享内存,那么Builder修改数据的时候集合怎么能不变化呢?这是因为ImmutableList的内部数据结构是树,只需要在更新集合的时候创建一个新的引用包含不同节点的引用即可。内部的实现原理,我会在下一篇博文中继续探讨。上面的代码既没有修改原来的IMMUTABLE COLLECTION也没有拷贝整个集合的内部操作。运行结果如下:

1.4  性能(PERFORMANCE

immutable collections 在很多方面,性能优于可变集合。当然性能上的优势和可变还是不可变的关系并不大,主要原因在于immutable collections内部的数据结构。比如下面的代码:

    private List<T> collection;

    

     public IReadOnlyList<int> SomeProperty

     {

         get

         {

             lock (this)

             {

                 return this.collection.ToList();

             }

         }

     }

每次访问都会引起内存拷贝的操作。

但是如果使用immutable collection就可以避免这个问题:

private ImmutableList<T> collection;

    public IReadOnlyList<int> SomeProperty

    {

        get { return this.collection; }

    }

内部原理和对性能的影响放在下一篇博客探讨,下面的列表是在算法复杂度层面的对比:

 

Mutable (amortized)

Mutable (worst case)

Immutable

Stack.Push

O(1)

O(n)

O(1)

Queue.Enqueue

O(1)

O(n)

O(1)

List.Add

O(1)

O(n)

O(log n)

HashSet.Add

O(1)

O(n)

O(log n)

SortedSet.Add

O(log n)

O(n)

O(log n)

Dictionary.Add

O(1)

O(n)

O(log n)

SortedDictionary.Add

O(log n)

O(n log n)

O(log n)

在内存使用方面,Immutable Collections要比可变类型的集合要多,空间换时间,这个世界上没有两全其美的事情。

小结

本篇博文只是浅尝则止,从概念上为您介绍了Immutable Collections的基本定义,简单应用。

我并没有拿典型的应用场景来举例,但是您可以从它们的线程安全,性能,内存使用等特性上权衡使用。如果有机会,我会将我的实际应用场景分享给您。

接下来,在下一篇博客中,我会探讨Immutable Collections的内部原理。也许你现在不会使用.NET4.5,但是其内部原理却是和平台无关的。

参考资料:

http://blogs.msdn.com/b/bclteam/archive/2013/03/19/inner-workings-of-immutable-collections-on-channel-9.aspx

http://blogs.msdn.com/b/bclteam/archive/2012/12/18/preview-of-immutable-collections-released-on-nuget.aspx

http://blogs.msdn.com/b/ericlippert/archive/2007/11/13/immutability-in-c-part-one-kinds-of-immutability.aspx

http://blogs.msdn.com/b/andrewarnottms/archive/2011/08/22/read-only-frozen-and-immutable-types-and-collections.aspx

 


本文转自玄魂博客园博客,原文链接:http://www.cnblogs.com/xuanhun/archive/2013/04/26/3045448.html,如需转载请自行联系原作者

目录
相关文章
|
1天前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
1059 0
|
10天前
|
人工智能 运维 安全
|
1天前
|
弹性计算 Kubernetes jenkins
如何在 ECS/EKS 集群中有效使用 Jenkins
本文探讨了如何将 Jenkins 与 AWS ECS 和 EKS 集群集成,以构建高效、灵活且具备自动扩缩容能力的 CI/CD 流水线,提升软件交付效率并优化资源成本。
242 0
|
8天前
|
人工智能 异构计算
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!
|
9天前
|
人工智能 测试技术 API
智能体(AI Agent)搭建全攻略:从概念到实践的终极指南
在人工智能浪潮中,智能体(AI Agent)正成为变革性技术。它们具备自主决策、环境感知、任务执行等能力,广泛应用于日常任务与商业流程。本文详解智能体概念、架构及七步搭建指南,助你打造专属智能体,迎接智能自动化新时代。
|
9天前
|
机器学习/深度学习 人工智能 自然语言处理
B站开源IndexTTS2,用极致表现力颠覆听觉体验
在语音合成技术不断演进的背景下,早期版本的IndexTTS虽然在多场景应用中展现出良好的表现,但在情感表达的细腻度与时长控制的精准性方面仍存在提升空间。为了解决这些问题,并进一步推动零样本语音合成在实际场景中的落地能力,B站语音团队对模型架构与训练策略进行了深度优化,推出了全新一代语音合成模型——IndexTTS2 。
736 23