0

0

c# 在 C# 中实现一个异步的 Lazy (AsyncLazy)

星降

星降

发布时间:2026-01-08 10:59:49

|

275人浏览过

|

来源于php中文网

原创

不能直接用Lazy实现异步延迟初始化,因其仅懒加载Task对象而不控制执行时机;AsyncLazy通过Lazy确保首次访问才执行且线程安全,工厂函数须返回未启动的Task,禁止在其中await。

c# 在 c# 中实现一个异步的 lazy<t> (asynclazy<t>)

为什么不能直接用 Lazy>

很多人第一反应是套一层 Lazy>,比如:

var lazyTask = new Lazy>(() => FetchDataAsync());
这看似异步延迟初始化,但问题在于:它只懒加载 任务对象本身,不控制任务的 执行时机。一旦你调用 lazyTask.Value,它就立刻返回一个已启动(可能已完成、正在运行或已失败)的 Task,无法保证「首次访问时才真正开始执行」——尤其当多个线程并发访问时,FetchDataAsync() 可能被多次触发,违背 lazy 语义。

AsyncLazy 必须确保首次访问才执行且线程安全

核心诉求是:第一次调用 .Value.Wait() / await 时,才真正启动异步工作,并且所有后续并发访问都复用同一个 Task,不重复执行。推荐做法是内部封装一个 Task 字段 + object 锁 + 双重检查(或直接用 Lazy> 配合惰性构造函数)。最简健壮实现如下:

public class AsyncLazy
{
    private readonly Lazy> _lazy;

    public AsyncLazy(Func> factory)
    {
        _lazy = new Lazy>(() => factory());
    }

    public Task Value => _lazy.Value;

    public async Task GetValueAsync() => await Value;
}

关键点:

闻君电脑报价系统
闻君电脑报价系统

一个实用于电脑系列产品报价的网站内容管理系统,傻瓜式地安装后,就有了一个类似于中关村 基本特点有: a).安装简便,傻瓜式的安装。 b).有一定的智能化,管理员管理发布信息都极其方便。 c).功能比较强大,该有的功能都有了,且有一些独特实用的功能,没有的功能,只要您提出合理,都会改进,现在还在改进中... d).后台相当完善,决不亚于任一个CMS系统。 e).定制性强,采用模板制,会有大

下载
  • _lazyLazy>,其 Value 属性保证只执行一次工厂函数
  • 工厂函数 Func> 返回的是未启动的 Task(如 Task.Run(...)HttpClient.GetStringAsync(...)),.NET 的 Task 构造即启动,所以必须确保工厂函数每次返回的是“新创建的、尚未 await 的 task”
  • 不要在工厂里 await —— 否则 Lazy.Value 会同步阻塞,失去异步意义

如何正确使用 AsyncLazy 并避免常见错误

错误示范:

var bad = new AsyncLazy(() => { 
    var result = await GetDataFromApi(); // ❌ 编译不过:lambda 不能是 async
    return result; 
});
正确写法是把 async 提到外部,用 Task.Run 或直接返回可等待的异步方法调用(前提是该方法返回 Task):

  • ✅ 直接传异步方法组:new AsyncLazy(GetDataFromApi)
  • ✅ 匿名函数中不 await,只返回 task:() => GetDataFromApi()
  • ✅ 若需组合逻辑,用 Task.Run(注意:仅限 CPU-bound 场景):() => Task.Run(() => ExpensiveCalculation()).ContinueWith(t => t.Result.ToString())
  • ❌ 不要用 Task.FromResult 包装同步结果再声称是 AsyncLazy —— 它失去了异步延迟的意义

调用时也注意:如果只是想等结果,用 await lazy.Value;如果需要同步阻塞(极少见),用 lazy.Value.GetAwaiter().GetResult(),而非 .Value.Result(可能死锁)。

要不要支持取消和异常缓存?

标准 AsyncLazy 不内置取消令牌,因为 Lazy 本身也不支持。如需 CancellationToken,必须扩展构造函数并把 token 传入工厂,例如:

public AsyncLazy(Func> factory, CancellationToken cancellationToken = default)
{
    _lazy = new Lazy>(() => factory(cancellationToken));
}
但要注意:如果工厂抛出异常,Lazy> 会缓存该异常 task,后续访问仍抛相同异常 —— 这是预期行为(类似 Lazy 缓存异常)。若需重试,就得自己封装 retry 逻辑,不在 AsyncLazy 职责范围内。

真正容易被忽略的是:AsyncLazy 的生命周期管理。它持有的 Task 不会自动释放,如果工厂返回的是长时运行或资源密集型 task(如未关闭的 HttpClient 请求),要确保上层控制好它的存活时间,必要时用 IDisposable 包装或依赖注入作用域来约束。

相关专题

更多
登录token无效
登录token无效

登录token无效解决方法:1、检查token的有效期限,如果token已经过期,需要重新获取一个新的token;2、检查token的签名,如果签名不正确,需要重新获取一个新的token;3、检查密钥的正确性,如果密钥不正确,需要重新获取一个新的token;4、使用HTTPS协议传输token,建议使用HTTPS协议进行传输 ;5、使用双因素认证,双因素认证可以提高账户的安全性。

6070

2023.09.14

登录token无效怎么办
登录token无效怎么办

登录token无效的解决办法有检查Token是否过期、检查Token是否正确、检查Token是否被篡改、检查Token是否与用户匹配、清除缓存或Cookie、检查网络连接和服务器状态、重新登录或请求新的Token、联系技术支持或开发人员等。本专题为大家提供token相关的文章、下载、课程内容,供大家免费下载体验。

794

2023.09.14

token怎么获取
token怎么获取

获取token值的方法:1、小程序调用“wx.login()”获取 临时登录凭证code,并回传到开发者服务器;2、开发者服务器以code换取,用户唯一标识openid和会话密钥“session_key”。想了解更详细的内容,可以阅读本专题下面的文章。

1054

2023.12.21

token什么意思
token什么意思

token是一种用于表示用户权限、记录交易信息、支付虚拟货币的数字货币。可以用来在特定的网络上进行交易,用来购买或出售特定的虚拟货币,也可以用来支付特定的服务费用。想了解更多token什么意思的相关内容可以访问本专题下面的文章。

1180

2024.03.01

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

476

2023.08.10

java学习网站推荐汇总
java学习网站推荐汇总

本专题整合了java学习网站相关内容,阅读专题下面的文章了解更多详细内容。

3

2026.01.08

java学习网站汇总
java学习网站汇总

本专题整合了java学习网站相关内容,阅读专题下面的文章了解更多详细内容。

0

2026.01.08

正则表达式 删除
正则表达式 删除

本专题整合了正则表达式删除教程大全,阅读专题下面的文章了解更多详细教程。

11

2026.01.08

java 元空间 永久代
java 元空间 永久代

本专题整合了java中元空间和永久代的区别,阅读专题下面的文章了解更多详细内容。

3

2026.01.08

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
nginx浅谈
nginx浅谈

共15课时 | 0.8万人学习

前端开发(基础+实战项目合集)
前端开发(基础+实战项目合集)

共60课时 | 3.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号