讲师中心 微信公众号
AI工具推荐 视频效率加速

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

老墨大大_6512

老墨大大_6512

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

|

295人浏览过

|

来源于php中文网

原创

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

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

为什么不能直接用 Lazy<task>></task>

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

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

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

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

public class AsyncLazy<T>
{
    private readonly Lazy<Task<T>> _lazy;

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

    public Task<T> Value => _lazy.Value;

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

关键点:

C#
C#

"避开 C# 常见错误——空陷阱、异步陷阱、LINQ 陷阱及资源泄漏。"

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

如何正确使用 AsyncLazy<t></t> 并避免常见错误

错误示范:

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

  • ✅ 直接传异步方法组:new AsyncLazy<string>(GetDataFromApi)</string>
  • ✅ 匿名函数中不 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<t></t> 不内置取消令牌,因为 Lazy<t></t> 本身也不支持。如需 CancellationToken,必须扩展构造函数并把 token 传入工厂,例如:

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

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

热门AI工具

更多
WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

创客贴AI

创客贴AI是一款AI图像与设计工具,AI辅助的智能在线设计工具。

音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

花生AI
花生AI Hot

一款AI视频创作工具,主要用于B站推出的AI视频创作工具,适合需要提升相关任务效率的用户。

相关专题

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

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

14526

2023.09.14

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

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

1705

2023.09.14

token怎么获取
token怎么获取

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

4017

2023.12.21

token什么意思
token什么意思

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

6181

2024.03.01

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

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

4498

2023.08.10

Vite 项目如何打包和部署
Vite 项目如何打包和部署

Vite项目打包部署专题汇集生产构建、产物预览、输出目录、资源路径、Nginx与GitHub Pages部署及常见白屏和路由404问题的已审核文章,帮助读者完成上线流程。

60

2026.10.28

Vite 如何搭配 Vue.js
Vite 如何搭配 Vue.js

Vite搭配Vue.js专题汇集创建Vue 3项目、配置开发环境、路径别名、Vue插件、代理、多环境构建与调试的已审核文章,帮助读者完成从初始化到日常开发的配置。

40

2026.10.28

Vite 项目目录怎么看
Vite 项目目录怎么看

Vite项目目录专题汇集入口文件、src源码、public静态资源、路径别名、项目根目录和构建输出目录相关的已审核文章,帮助读者理解各文件夹的用途与引用方式。

160

2026.10.28

Gradle插件开发入门教程
Gradle插件开发入门教程

本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

140

2026.10.22

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
前端开发(基础+实战项目合集)
前端开发(基础+实战项目合集)

共60课时 | 6.6万人学习

第十九期_前端开发
第十九期_前端开发

共111课时 | 19.2万人学习

第十七期_前端开发
第十七期_前端开发

共60课时 | 10.1万人学习

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

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn