【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 提供的在不同方案中启用迟缓初始化的类型。
类型 | 说明 |
---|---|
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> 类的新实例。 当延迟初始化发生时,将使用指定的初始化函数和初始化模式。 传入方法,用来return value。 |
Lazy<T>(Func<T>, LazyThreadSafetyMode) |
初始化 Lazy<T> 类的新实例,它使用指定的初始化函数和线程安全模式。 传入方法,用来return value。 |
Lazy<T>(LazyThreadSafetyMode) |
初始化 Lazy<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> 类
该类相当与一个线程结界,将变量的值和作用限制在线程中。所以用该类包装过的类型是线程安全的。该类包装过的类型变量的只能在该线程中使用,其他线程包括子线程无法使用。
ThreadLoal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本。这里有几点需要注意:
- 因为每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。这是也是 ThreadLocal 命名的由来。
- 既然每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题。
ThreadLocal 提供了线程本地的实例。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。ThreadLocal 变量通常被private static修饰。当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。
总的来说,ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景。
构造函数
ThreadLocal<T>() |
初始化 ThreadLocal<T> 实例。 |
ThreadLocal<T>(Boolean) |
初始化 ThreadLocal<T> 实例并指定是否可从任意线程访问所有值。 |
ThreadLocal<T>(Func<T>) |
使用指定的 |
ThreadLocal<T>(Func<T>, Boolean) |
使用指定的 传入方法 用来return value |
属性
IsValueCreated |
获取是否在当前线程上初始化 Value。用法类似lazy<T> |
Value |
数据类型的默认值或者方法返回值。用法类似lazy<T> |
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)
【C# 线程】 延迟初始化的更多相关文章
- .NET多线程之线程安全,Lock(锁)、Monitor(同步访问)、LazyInitializer(延迟初始化)、Interlocked(原子操作)、static(静态)构造函数、volatile、
1.什么是线程安全 线程安全是编程中的术语,指某个函数.函数库在多线程环境中被调用时,能够正确地处理多个线程之间的共享变量,使程序功能正确完成.一般来说,线程安全的函数应该为每个调用它的线程分配专门的 ...
- C#性能优化之Lazy<T> 实现延迟初始化
在.NET4.0中,可以使用Lazy<T> 来实现对象的延迟初始化,从而优化系统的性能.延迟初始化就是将对象的初始化延迟到第一次使用该对象时.延迟初始化是我们在写程序时经常会遇到的情形,例 ...
- C#性能优化:延迟初始化Lazy<T>
1. 概述 我们创建某一个对象需要很大的消耗,而这个对象在运行过程中又不一定用到,为了避免每次运行都创建该对象,这时候延迟初始化(也叫延迟实例化)就出场了. 延迟初始化出现于.NET 4.0,主要用于 ...
- C# 延迟初始化
一个对象的延迟初始化意味着该对象的创建将会延迟至第一次使用该对象时.(在本主题中,术语“延迟初始化”和“延迟实例化”是同义词.)延迟初始化主要用于提高性能,避免浪费计算,并减少程序内存要求. 以下是最 ...
- Lazy<T>延迟初始化
延迟初始化:Lazy<T> 1. 概述 我们创建某一个对象需要很大的消耗,而这个对象在运行过程中又不一定用到,为了避免每次运行都创建该对象,这时候延迟初始化(也叫延迟实例化)就出场了. 延 ...
- 双重检查锁定与延迟初始化(转自infoq)
很好的文章,转自http://www.infoq.com/cn/articles/double-checked-locking-with-delay-initialization 在java程序中,有 ...
- Effective Java 第三版——83. 明智谨慎地使用延迟初始化
Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...
- 延迟初始化Lazy
延迟初始化出现于.NET 4.0,主要用于提高性能,避免浪费计算,并减少程序内存要求.也可以称为,按需加载. 基本语法: Lazy<T> xx = new Lazy<T>(); ...
- volatile双重检查锁定与延迟初始化
一.基本概念: 1.volatile是轻量级的synchronized,在多核处理器开发中保证了共享变量的“可见性”.可见性的意思是,当一个线程修改一个共享变量时,另一个线程能读到这个修改的值. 2. ...
随机推荐
- Windows蓝牙失效超全攻略
新电脑蓝牙出现问题,我捣鼓了很久,历经九九八十一难得以修复,说一说我在网上看到的各种方法. 一个功能正常使用,需要经过一个又一个的步骤.任何一个地方出问题,都有可能造成蓝牙失效.以下方法按出现概率从大 ...
- Cesium入门1 - Cesium介绍
Cesium入门1 - Cesium介绍 Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ Cesium简介 Ce ...
- dubbo系列一、dubbo启动流程
目录 dubbo启动流程分析记录 一.dubbo provider启动流程 1.自动装配 2.ServiceBean处理 3.服务暴露export() 3.1.检测dubbo.xxx.配置属性,配置到 ...
- C# 代码性能优化举例
普通人如果说什么事情慢,指的是 5 分钟,10 分钟,或者 1 个小时.2 个小时.而程序员要说什么事情慢,他们指的是 2 纳秒. 每个纳秒对程序员来说都是非常宝贵的,所以,要对代码进行优化,优化,再 ...
- php中关于数据库的操作
php中对数据库的操作,举例如下: //连接到本地mysql数据库 $myconn=mysql_connect("localhost","root"," ...
- gin框架中使用jwt
生成解析token 如今有很多将身份验证内置到API中的方法 -JSON Web令牌只是其中之一.JSON Web令牌(JWT)作为令牌系统而不是在每次请求时都发送用户名和密码,因此比其他方法(如基本 ...
- Ali云组件概念
- 使用pybind11为Python编写C++扩展(一)配置篇:Build(编译和链接)
目录 Setuptools CMake 最后决定选用pybind11,理由如下: 比python原生的C API看起来人性多了 我的C++代码不是现成的,需要一定的C++开发工作量,所以感觉cytho ...
- aws 试题
/* Domain 1 Design Resilient Architectures 1. Which of the following statements regarding S3 storage ...
- 计算机网络再次整理————tcp例子[五]
前言 本文介绍一些tcp的例子,然后不断完善一下. 正文 服务端: // See https://aka.ms/new-console-template for more information us ...