【C# 线程】 延迟初始化

 

1. 简介

1、延迟初始化出现于.NET 4.0,主要用于提高性能,避免浪费计算,并减少程序内存要求。也可以称为,按需加载。

2、从net 4.0开始,C#开始支持延迟初始化,通过Lazy关键字,我们可以声明某个对象为仅仅当第一次使用的时候,再初始化,如果一直没有调用,那就不初始化,省去了一部分不必要的开销,提升了效率,同时Lazy是天生线程安全的

3、注:Lazy<T> 对象初始化默认是线程安全的,在多线程环境下,第一个访问 Lazy<T> 对象的 Value 属性的线程将初始化 Lazy<T> 对象,以后访问的线程都将使用第一次初始化的数据。

2. 应用场景

  • 1、大对象和耗时对象

    对象创建成本高且程序可能不会使用它。 例如,假定内存中有具有 Orders 属性的 Customer 对象,该对象包含大量 Order 对象,初始化这些对象需要数据库连接。 如果用户永远不要求显示 Orders 或在计算中使用该数据,则无需使用系统内存或计算周期来创建它。 通过使用 Lazy<Orders> 来声明 Orders 对象用于迟缓初始化,可以避免在不使用该对象时浪费系统资源。对象创建成本高,且希望将其创建推迟到其他高成本操作完成后。 例如,假定程序在启动时加载多个对象实例,但是只需立即加载其中一部分。 可以通过推迟初始化不需要的对象,直到创建所需对象,提升程序的启动性能。

  • 2、封装方法

    将委托或者方法对象保存,并在需要的时候调用。

虽然可以编写自己的代码来执行迟缓初始化,但我们建议使用 Lazy<T>Lazy<T> 及其相关的类型还支持线程安全并提供一致的异常传播策略。

下表列出了 .NET Framework 版本 4 提供的在不同方案中启用迟缓初始化的类型。

表 1
类型 说明
Lazy<T> 为任何类库或用户定义类型提供迟缓初始化语义的包装类。
ThreadLocal<T> 类似于 Lazy<T>,除了该包装类基于线程本地提供迟缓初始化语义。 每个线程都可以访问自己唯一的值。
LazyInitializer 为对象的迟缓初始化提供高级 static(Visual Basic 中的 Shared)方法,无需支付类的成本。

 

 

 

 

 

 

 

Lazy构造函数

构造函数
Lazy<T>()

初始化 Lazy<T> 类的新实例。 发生迟缓初始化时,使用目标类型的无参数构造函数。

Lazy<T>(Boolean)

初始化 Lazy<T> 类的新实例。 发生迟缓初始化时,使用目标类型的无参数构造函数和指定的初始化模式。

Lazy<T>(Func<T>)

初始化 Lazy<T> 类的新实例。 出现迟缓初始化时,将使用指定的初始化函数。

Lazy<T>(Func<T>, Boolean)

初始化 Lazy<T> 类的新实例。 当延迟初始化发生时,将使用指定的初始化函数和初始化模式。

Lazy<T>(Func<T>, LazyThreadSafetyMode)

初始化 Lazy<T> 类的新实例,它使用指定的初始化函数和线程安全模式。

Lazy<T>(LazyThreadSafetyMode)

初始化 Lazy<T> 类的新实例,其中使用 T 的无参数构造函数和指定的线程安全性模式。

Lazy<T>(T)

初始化 Lazy<T> 类的新实例,该类使用已预先初始化的指定值。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Lazy.Value属性的使用

Lazy对象创建后,并不会立即创建对应的对象,一旦使用.Vale,那么对应的变量就会被实例化,IsValueCreated属性也就变成了true。

Value属性是只读的,也就意味着如果Value存储了引用类型,将无法为其分配新对象,只可以更改此对象公共的属性或者字段等,如果Value存储的是值类型,那么就不能修改其值了,只能通过再次调用变量的函数使用新的参数来创建新的变量

在Lazy对象创建后,在首次访问变量的Value属性前,

Lazy.IsValueCreated属性的使用

判断是实例化

LazyThreadSafetyMode 枚举

指定 Lazy<T> 实例如何同步多个线程间的访问。

 

所有此类构造函数都是完全线程安全的。

在内部使用锁,则可能会发生死锁

ExecutionAndPublication  
不是线程安全的。 None false 不适用。
完全线程安全;用于初始化值的线程争用。 PublicationOnly

 

 

 

 

构造时使用默认的初始化方式

using System;

namespace LazyUsage
{
    class LazyDemo
    {
        static void Main()
        {
            Lazy<Data> lazyData = new Lazy<Data>();
            Console.WriteLine("Main->is lazyData Initialized? value = " + lazyData.IsValueCreated);
            lazyData.Value.Print();//此处访问时才会将Data真正的初始化
            Console.WriteLine("Main->is lazyData Initialized? value = " + lazyData.IsValueCreated);

            Console.ReadKey();
        }
    }

    class Data
    {
        public Data() 
        {
            Console.WriteLine("Data::.ctor->Initialized");
        }

        public void Print()
        {
            Console.WriteLine("Data::Print->println");
        }
    }
}

输出结果

Main->is lazyData Initialized? value = False
Data::.ctor->Initialized
Data::Print->println
Main->is lazyData Initialized? value = True

构造时使用指定的委托初始化

using System;

namespace LazyUsage
{
    class LazyDemo
    {
        static void Main()
        {
            //指定委托来初始化Data
            Lazy<Data> lazyData = new Lazy<Data>(
                () =>
                {
                    Console.WriteLine("Main->lazyData will be Initialized!");
                    return new Data("Test");
                });
            Console.WriteLine("Main->is lazyData Initialized? value = " + lazyData.IsValueCreated);
            lazyData.Value.Print();
            Console.WriteLine("Main->is lazyData Initialized? value = " + lazyData.IsValueCreated);


            Console.ReadKey();
        }
    }

    class Data
    {
        public string Name { get; private set; }

        public Data(string name)
        {
            Name = name;
            Console.WriteLine("Data::.ctor->Initialized,name = "+name);
        }

        public void Print()
        {
            Console.WriteLine("Data::Print->name = " + Name);
        }
    }
}

输出结果

Main->is lazyData Initialized? value = False
Main->lazyData will be Initialized!
Data::.ctor->Initialized,name = Test
Data::Print->name = Test
Main->is lazyData Initialized? value = True

 

扩展. 实现延迟属性

class Customer
{
    private Lazy<Orders> _orders;

    public string CustomerID {get; private set;}

    public Customer(string id)
    {
        CustomerID = id;
        _orders = new Lazy<Orders>(() =>
        {
            return new Orders(this.CustomerID);
        });
    }

    public Orders MyOrders
    {
        get
        {
            return _orders.Value;
        }
    }
}

 ThreadLocal<T> 类

该类相当与一个线程结界,将变量的值和作用限制在线程中。所以用该类包装过的类型是线程安全的。该类包装过的类型变量的只能在该线程中使用,其他线程包括子线程无法使用。

构造函数

构造函数
ThreadLocal<T>()

初始化 ThreadLocal<T> 实例。

ThreadLocal<T>(Boolean)

初始化 ThreadLocal<T> 实例并指定是否可从任意线程访问所有值。

ThreadLocal<T>(Func<T>)

使用指定的 valueFactory 函数初始化 ThreadLocal<T> 实例。

ThreadLocal<T>(Func<T>, Boolean)

使用指定的 valueFactory 函数和指示是否可从任意线程访问所有值的标志初始化 ThreadLocal<T> 实例。

 

 

 

 

 

属性

属性
IsValueCreated

获取是否在当前线程上初始化 Value

Value

获取或设置当前线程的此实例的值。

Values

获取当前由已经访问此实例的所有线程存储的所有值的列表。

 

 

 

 

 

 

 

提供线程数据的本地存储。

static void Main(string[] args)
{
     ThreadLocal<int> threadLocal = new ThreadLocal<int>();
     //在主线程这个变量值为1
     threadLocal.Value = 1;
     new Thread(() => Console.WriteLine($"托管线程ID:{Thread.CurrentThread.ManagedThreadId} 值为:{threadLocal.Value++}")).Start();
     new Thread(() => Console.WriteLine($"托管线程ID:{Thread.CurrentThread.ManagedThreadId} 值为:{threadLocal.Value++}")).Start();
     new Thread(() => Console.WriteLine($"托管线程ID:{Thread.CurrentThread.ManagedThreadId} 值为:{threadLocal.Value++}")).Start();
     Console.WriteLine($"主线程ID:{Thread.CurrentThread.ManagedThreadId} 值为:{threadLocal.Value}");
}

 输出结果:
托管线程ID:10 值为:0
主线程ID:1 值为:1
托管线程ID:11 值为:0
托管线程ID:12 值为:0

 

using System;
using System.Threading;
using System.Threading.Tasks;

class ThreadLocalDemo
{
    
        // Demonstrates:
        //      ThreadLocal(T) constructor
        //      ThreadLocal(T).Value
        //      One usage of ThreadLocal(T)
        static void Main()
        {
            // Thread-Local variable that yields a name for a thread
            ThreadLocal<string> ThreadName = new ThreadLocal<string>(() =>
            {
                return "Thread" + Thread.CurrentThread.ManagedThreadId;
            });

            // Action that prints out ThreadName for the current thread
            Action action = () =>
            {
                // If ThreadName.IsValueCreated is true, it means that we are not the
                // first action to run on this thread.
                bool repeat = ThreadName.IsValueCreated;

                Console.WriteLine("ThreadName = {0} {1}", ThreadName.Value, repeat ? "(repeat)" : "");
            };

            // Launch eight of them.  On 4 cores or less, you should see some repeat ThreadNames
            Parallel.Invoke(action, action, action, action, action, action, action, action);

            // Dispose when you are done
            ThreadName.Dispose();
        }
}
// This multithreading example can produce different outputs for each 'action' invocation and will vary with each run.
// Therefore, the example output will resemble but may not exactly match the following output (from a 4 core processor):
// ThreadName = Thread5 
// ThreadName = Thread6 
// ThreadName = Thread4 
// ThreadName = Thread6 (repeat)
// ThreadName = Thread1 
// ThreadName = Thread4 (repeat)
// ThreadName = Thread7 
// ThreadName = Thread5 (repeat)

 

上一篇:设计模式之单例模式


下一篇:正则表达式普遍风格