当前位置:

首页 > 编程开发 > C#缓存机制详解与使用方法

C#缓存机制详解与使用方法

C#缓存机制核心是提升性能、降低延迟、减轻后端压力,主要分为应用内缓存(IMemoryCache)、分布式缓存(如Redis)和HTTP缓存;常用策略包括绝对过期、滑动过期、LRU淘汰等,结合Cache-Aside模式管理更新,通过设置合理过期时间、使用布隆过滤器防穿透、加锁防击穿、错峰过期防雪崩,并配合监控命中率与延迟,确保缓存高效稳定。

C#缓存机制核心是提升性能、降低延迟、减轻后端压力,主要分为应用内缓存(IMemoryCache)、分布式缓存(如Redis)和HTTP缓存;常用策略包括绝对过期、滑动过期、LRU淘汰等,结合Cache-Aside模式管理更新,通过设置合理过期时间、使用布隆过滤器防穿透、加锁防击穿、错峰过期防雪崩,并配合监控命中率与延迟,确保缓存高效稳定。

C#的缓存机制是什么?如何使用?

C#的缓存机制,说白了,就是把那些计算成本高、或者需要频繁访问的数据,暂时存起来,下次再要的时候,就不用重新计算或查询了,直接拿来用。这就像你经常去的那家咖啡店,知道你喜欢什么,提前给你准备好,省去了你每次点单的麻烦。至于怎么用,那就得看你具体的需求和场景了,从简单的内存缓存到分布式缓存,选择可不少,核心目的都是为了提升性能、降低延迟、减轻后端服务(比如数据库)的压力。

解决方案

在我看来,C# 里谈缓存,我们通常会想到两种主要类型:应用内缓存(In-Memory Cache)和分布式缓存(Distributed Cache)。当然,对于Web应用来说,HTTP缓存也是不可忽视的一环,虽然它更多是协议层面的事儿,但和C#应用紧密相关。

1. 应用内缓存 (In-Memory Cache)

这是最直接、最容易上手的缓存方式。在.NET Core/.NET 5+ 中,Microsoft.Extensions.Caching.Memory 这个库提供了 IMemoryCache 接口和其默认实现 MemoryCache。它就运行在你的应用程序进程里,所以访问速度极快,几乎没有网络延迟。

怎么用呢?通常你会通过依赖注入(DI)的方式获取 IMemoryCache 实例:

public class MyService
{
    private readonly IMemoryCache _cache;

    public MyService(IMemoryCache cache)
    {
        _cache = cache;
    }

    public async Task> GetProductsAsync()
    {
        // 尝试从缓存获取
        if (!_cache.TryGetValue("ProductList", out List products))
        {
            // 缓存中没有,从数据库或其他数据源加载
            products = await LoadProductsFromDatabaseAsync();

            // 设置缓存选项:比如1分钟绝对过期,或者30秒滑动过期
            var cacheEntryOptions = new MemoryCacheEntryOptions()
                .SetAbsoluteExpiration(TimeSpan.FromMinutes(1)) // 1分钟后绝对过期
                .SetSlidingExpiration(TimeSpan.FromSeconds(30)); // 30秒未访问则过期

            // 将数据存入缓存
            _cache.Set("ProductList", products, cacheEntryOptions);
        }
        return products;
    }

    private Task> LoadProductsFromDatabaseAsync()
    {
        // 模拟从数据库加载数据
        return Task.FromResult(new List
        {
            new Product { Id = 1, Name = "Laptop" },
            new Product { Id = 2, Name = "Mouse" }
        });
    }
}

别忘了在 Startup.csProgram.cs 里注册 IMemoryCache

// .NET 6+ Minimal API
builder.Services.AddMemoryCache();

// 或者在 Startup.cs 的 ConfigureServices 方法中
public void ConfigureServices(IServiceCollection services)
{
    services.AddMemoryCache();
    // ... 其他服务
}

它的优点很明显:简单、快速。但缺点也同样突出:缓存数据只存在于当前应用实例中,如果你的应用是多实例部署的,每个实例都会有自己独立的缓存,数据不共享。而且,一旦应用重启,所有缓存数据都会丢失。所以,它更适合那些对实时性要求没那么高,或者数据量不大、单机就能搞定的场景。

2. 分布式缓存 (Distributed Cache)

当你的应用需要横向扩展,或者缓存数据需要在多个服务间共享时,内存缓存就力不从心了。这时候,分布式缓存就成了标配。Redis 是目前最流行、最常用的分布式缓存解决方案之一,在 C# 中通常会使用 StackExchange.Redis 这个库来操作。

使用分布式缓存,你需要一个独立的缓存服务器(比如 Redis 服务器)。在 C# 代码中,你不再直接操作内存,而是通过网络请求去 Redis 服务器存取数据。

// .NET 6+ Minimal API
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = "localhost:6379"; // Redis 连接字符串
    options.InstanceName = "MyApp_"; // 缓存键前缀
});

// 或者在 Startup.cs 的 ConfigureServices 方法中
public void ConfigureServices(IServiceCollection services)
{
    services.AddStackExchangeRedisCache(options =>
    {
        options.Configuration = "localhost:6379,password=yourpassword"; // 带密码的连接
        options.InstanceName = "MyApp_";
    });
    // ... 其他服务
}

然后,在你的服务中注入 IDistributedCache 接口:

public class AnotherService
{
    private readonly IDistributedCache _distributedCache;

    public AnotherService(IDistributedCache distributedCache)
    {
        _distributedCache = distributedCache;
    }

    public async Task> GetProductsDistributedAsync()
    {
        string cacheKey = "DistributedProductList";
        string cachedProductsJson = await _distributedCache.GetStringAsync(cacheKey);

        if (string.IsNullOrEmpty(cachedProductsJson))
        {
            // 缓存中没有,从数据库加载
            var products = await LoadProductsFromDatabaseAsync();
            var jsonToCache = System.Text.Json.JsonSerializer.Serialize(products);

            // 设置分布式缓存选项
            var options = new DistributedCacheEntryOptions()
                .SetAbsoluteExpiration(TimeSpan.FromMinutes(5)); // 5分钟绝对过期

            await _distributedCache.SetStringAsync(cacheKey, jsonToCache, options);
            return products;
        }
        else
        {
            return System.Text.Json.JsonSerializer.Deserialize>(cachedProductsJson);
        }
    }

    private Task> LoadProductsFromDatabaseAsync()
    {
        // 模拟从数据库加载数据
        return Task.FromResult(new List
        {
            new Product { Id = 3, Name = "Keyboard" },
            new Product { Id = 4, Name = "Monitor" }
        });
    }
}

分布式缓存的优势在于可伸缩性、数据共享和高可用性。即使你的应用实例挂了,缓存数据依然存在于 Redis 中。但它也引入了额外的复杂性:你需要部署和维护 Redis 服务器,网络延迟也比内存缓存高。不过,对于现代微服务架构,这几乎是必选项。

3. HTTP 缓存

虽然不是 C# 语言层面的缓存,但对于 Web API 或 MVC 应用来说,HTTP 缓存至关重要。它通过 HTTP 响应头来指示客户端(浏览器、代理服务器)如何缓存响应。常见的头部有 Cache-ControlExpiresETagLast-Modified

在 C# Web 应用中,你可以通过 Response.Headers 来设置这些头部,或者使用 ASP.NET Core 提供的 ResponseCaching 中间件。

// 在 Startup.cs 的 ConfigureServices 中
services.AddResponseCaching();

// 在 Configure 方法中
app.UseResponseCaching();

// 在 Controller 或 Action 上使用特性
[ResponseCache(Duration = 60, Location = ResponseCacheLocation.Any)]
public IActionResult GetExpensiveData()
{
    // ... 返回数据
}

这能有效减少服务器负载和网络流量,尤其对于那些不经常变化的静态资源或公共数据。

C#应用中,何时应该考虑引入缓存机制?

在我看来,决定是否以及何时引入缓存,主要取决于几个关键因素,这就像你决定要不要在家里囤积物资一样,得看需求和成本。

  • 数据访问频率高且变化不频繁: 这是最经典的缓存场景。比如一个电商网站的商品分类列表,或者一个博客的置顶文章,这些数据被大量用户反复请求,但更新频率很低。每次都去数据库查,数据库压力会很大。
  • 数据计算或获取成本高昂: 有些数据可能需要复杂的计算(比如报表统计),或者需要调用外部API(网络延迟和调用次数限制),这些操作都非常耗时耗资源。把计算结果或API响应缓存起来,能显著提升响应速度。
  • 数据库或后端服务负载过重: 如果你的数据库服务器经常CPU飙高,或者出现连接池耗尽的情况,那么缓存就是你的救命稻草。通过缓存,可以把大部分读请求挡在数据库前面,大大减轻其压力。
  • 响应时间有严格要求: 用户对应用响应速度的期望越来越高。如果你的某个接口响应时间过长,导致用户体验不佳,缓存通常是最有效的优化手段之一。
  • 数据一致性要求不那么极致: 缓存总是会引入数据一致性的问题。如果你的业务允许在短时间内看到“稍微旧一点”的数据(比如几秒到几分钟的延迟),那么缓存就是个好选择。如果要求数据绝对实时一致,那缓存就得非常谨慎地设计,或者干脆不适合。

说实话,很多时候我们不是一开始就想着要用缓存,而是当性能瓶颈出现时,才开始考虑它。但这其实有点被动。在设计系统的时候,对那些“热点数据”和“慢查询”有个预判,提前规划缓存策略,往往能事半功倍。

C#中常见的缓存策略与淘汰机制有哪些?

缓存策略和淘汰机制,就像是管理你冰箱里的食物,得有章法,不然就乱套了。在 C# 的缓存世界里,这些机制决定了缓存项何时失效、何时被清理,以及在缓存容量不足时,哪些数据应该被“扔掉”。

  • 绝对过期 (Absolute Expiration): 这是最直接的策略。你给一个缓存项设定一个固定的生命周期,比如“10分钟后过期”。不管这10分钟内有没有人访问它,到点就失效。

    • 适用场景: 适用于那些你知道它在一段时间内不会变化,或者变化了也无所谓的配置数据、短时令牌等。
    • 示例: SetAbsoluteExpiration(TimeSpan.FromMinutes(10))
  • 滑动过期 (Sliding Expiration): 这个策略就“智能”多了。你设定一个时间间隔,比如“30秒”。如果一个缓存项在30秒内没有被访问,它就失效。但只要有人访问了它,它的生命周期就会被重置,重新开始计算30秒。

    • 适用场景: 适用于那些“热点”数据。只要有人用,它就一直活着;没人用,它就慢慢“凉”掉,腾出空间。这能有效避免不常用数据长期占据缓存。
    • 示例: SetSlidingExpiration(TimeSpan.FromSeconds(30))
  • 组合过期策略:IMemoryCache 中,你可以同时设置绝对过期和滑动过期。比如,你可以设置一个缓存项在1小时内绝对过期,但如果它在10分钟内没有被访问,就滑动过期。这意味着它最多存活1小时,但如果一直没人访问,10分钟后就没了。

  • 基于大小/数量的淘汰 (Size/Count Based Eviction): 当缓存的总容量(比如内存缓存的总字节数,或者缓存项的总数量)达到预设上限时,缓存管理器就需要选择一些缓存项进行清理。

    • 示例: MemoryCacheEntryOptions.SetSize(1),然后在 AddMemoryCache 时配置 SizeLimit
    • 淘汰算法: 这就涉及到一些经典的算法了:
      • LRU (Least Recently Used - 最近最少使用): 优先淘汰最近最少被访问的缓存项。这是最常见的淘汰策略之一,因为它假设过去不常用的数据,将来也不太常用。
      • LFU (Least Frequently Used - 最不经常使用): 优先淘汰在一段时间内被访问次数最少的缓存项。它更关注访问频率而非时间。
      • FIFO (First In, First Out - 先进先出): 优先淘汰最早进入缓存的项。这个比较简单粗暴,不考虑访问频率或时间。
  • 优先级淘汰 (Priority Eviction): 在某些缓存实现中,你可以给缓存项设置一个优先级(比如 CacheItemPriority.HighCacheItemPriority.Low)。当需要淘汰时,低优先级的项会被优先清理。

    • 适用场景: 确保关键数据即使在缓存紧张时也能尽可能保留。
  • 依赖项缓存 (Cache Dependencies): 这是一种非常强大的机制。你可以让一个缓存项依赖于其他资源,比如一个文件、一个数据库表中的某条记录,甚至是另一个缓存项。当这些依赖的资源发生变化时,依赖于它们的所有缓存项都会自动失效。

    • 示例: ChangeToken.OnChange 可以用来监听文件变化,然后使缓存失效。分布式缓存如 Redis 也可以通过发布/订阅模式实现类似效果。
    • 适用场景: 确保缓存数据与原始数据源的一致性,避免手动刷新缓存的麻烦。

选择哪种策略,真的要根据你的业务场景和数据特性来。没有银弹,只有最适合的。

如何有效地管理C#缓存的更新与失效?

缓存的生命周期管理,也就是如何让缓存数据保持“新鲜”,同时又不过度消耗资源,这绝对是缓存技术里最考验功力的地方。就像你家冰箱里的食物,你得知道哪些要尽快吃掉,哪些可以放久一点,过期了就得扔。

1. 缓存更新策略

  • Cache-Aside (旁路缓存/按需加载): 这是最常见、也最推荐的模式。它的逻辑是这样的:

    1. 应用程序首先尝试从缓存中读取数据。
    2. 如果缓存命中,直接返回数据。
    3. 如果缓存未命中,应用程序就从数据源(比如数据库)加载数据。
    4. 加载成功后,将数据存入缓存,然后返回给用户。
    • 优点: 简单直观,按需加载,避免缓存大量不被访问的数据。
    • 缺点: 第一次访问时会有缓存未命中延迟;更新数据时需要手动使缓存失效,可能导致短暂的数据不一致。
  • Read-Through (读穿/直读缓存): 应用程序从缓存中读取数据,如果缓存中没有,缓存系统会“自动”从数据源加载数据,并将其存入缓存,然后返回给应用程序。

    • 优点: 对应用程序透明,简化了应用代码。
    • 缺点: 需要缓存系统支持这种“自动加载”能力,实现起来相对复杂。
  • Write-Through (写穿/直写缓存): 应用程序向缓存写入数据,缓存系统同时将数据写入数据源。只有当数据源和缓存都写入成功后,才返回成功。

    • 优点: 保证了数据源和缓存的一致性。
    • 缺点: 写入操作的延迟会增加,因为要等待两个地方都写入成功。
  • Write-Behind (写回/回写缓存): 应用程序向缓存写入数据,缓存系统立即返回成功。然后,缓存系统会异步地将数据写入数据源。

    • 优点: 写入速度快,应用程序响应迅速。
    • 缺点: 如果缓存系统在数据写入数据源之前崩溃,可能会导致数据丢失。数据一致性是最终一致性。
  • 主动刷新/预热 (Proactive Refresh/Warm-up): 通过定时任务或其他机制,在数据失效前或在系统启动时,提前加载或刷新缓存中的关键数据。

    • 优点: 保证热点数据始终在缓存中,减少用户首次访问的延迟。
    • 缺点: 需要额外的逻辑来管理刷新任务,可能造成不必要的资源消耗(如果刷新了不被访问的数据)。

2. 缓存失效管理

这是确保数据“新鲜度”的关键。

  • 基于时间的过期: 前面提到的绝对过期和滑动过期就是这种。这是最简单也最常用的方式。

  • 基于事件的失效: 当底层数据源发生变化时,主动通知缓存使其失效。

    • 数据库变更通知: 比如使用 SQL Server 的 SqlDependency,或者通过消息队列(如 Kafka、RabbitMQ)订阅数据库的 CDC (Change Data Capture) 事件,当数据更新时,发布消息通知相关服务清理缓存。
    • 文件变更通知: 使用 FileSystemWatcher 监听文件变化,一旦文件更新,就让依赖该文件的缓存失效。
    • 手动清除: 提供一个管理界面或API,允许管理员手动清除特定缓存或所有缓存。这在紧急情况下非常有用。
  • 缓存穿透、击穿与雪崩的应对

    这三个是缓存领域常见的问题,处理不好会直接拖垮你的后端服务。

    • 缓存穿透 (Cache Penetration): 当用户请求一个既不在缓存中,也不在数据库中的数据时,每次请求都会穿透缓存,直接打到数据库,导致数据库压力剧增。

      • 应对:
        • 缓存空值: 如果查询结果为空,也把这个空结果缓存起来,并设置一个较短的过期时间。下次再有相同请求时,直接返回空,不再查询数据库。
        • 布隆过滤器 (Bloom Filter): 在缓存层前加一个布隆过滤器,快速判断请求的数据是否存在。如果布隆过滤器说不存在,那就直接拒绝请求,连缓存都不用查。
    • 缓存击穿 (Cache Breakdown): 某个热点数据(访问量非常高的数据)在缓存中过期了,此时大量请求同时涌入,都发现缓存未命中,然后都去查询数据库,导致数据库瞬间被打垮。

      • 应对:
        • 加锁: 在从数据库加载数据并回填缓存时,对该缓存项加锁。只允许一个请求去查询数据库并回填缓存,其他请求等待或返回旧数据。
        • 永不过期: 对于特别核心的热点数据,可以考虑将其设置为永不过期,或者设置一个非常长的过期时间,然后通过异步任务定期刷新。
        • 二级缓存: 在分布式缓存之上再加一层内存缓存,即使分布式缓存过期,内存缓存还能顶一会儿。
    • 缓存雪崩 (Cache Avalanche): 缓存中的大量数据在同一时间集中过期,导致所有请求都涌向数据库,数据库瞬间崩溃。

      • 应对:
        • 错开过期时间: 给不同的缓存项设置随机的过期时间,避免大量缓存同时失效。
        • 多级缓存: 引入多级缓存,比如内存缓存 + Redis 缓存,即使一级缓存失效,还有二级缓存顶着。
        • 熔断与降级: 当数据库压力过大时,启动熔断机制,拒绝部分请求或返回默认值,保护数据库。
        • 限流: 控制对数据库的并发请求数量。

3. 监控与指标

无论你用哪种缓存,监控都是不可或缺的。你需要关注:

  • 缓存命中率 (Cache Hit Ratio): 缓存命中次数 / 总请求次数。这是衡量缓存效果最重要的指标。
  • 缓存失效率 (Cache Miss Ratio): 缓存未命中次数 / 总请求次数。
  • 缓存占用内存/磁盘空间: 避免缓存过度消耗资源。
  • 缓存操作延迟: 存取缓存的平均耗时。
  • **淘汰率:
本文内容来源于互联网,如有侵权请联系删除。
作者最新文章
编程开发
相关文章 更多
C++动态数组初始化怎么写?常用语句与代码示例
C++动态数组初始化怎么写?常用语句与代码示例

深入解析C++中动态数组的初始化机制,涵盖new操作符的不同用法、基本类型与类对象的初始化差异,以及为何在现代C++开发中应优先使用std::vector。

using namespace 使用中遇到的问题怎么解决
using namespace 使用中遇到的问题怎么解决

命名空间的基本概念与常见引入问题在C++等编程语言中,命名空间(namespace)是一种将代码标识符(如变量、函数、类名)封装在特定名称下的机制,其主要目的是避免命名冲突,尤其是在大型项目或使用多个第三方库时。使用“using namespace”指令可以将指定命名空间中的所有名称引入当前作用域,

c语言函数递归 实操经验总结:这些技巧很实用
c语言函数递归 实操经验总结:这些技巧很实用

理解递归的基本原理在C语言中,递归是一种函数调用自身的编程技术。要掌握它,首先需要理解其核心思想:将一个复杂的大问题,分解为一个或几个与原问题相似但规模更小的子问题,直到子问题足够简单,可以直接求解。这个过程通常包含两个关键部分:递归出口和递归体。递归出口定义了问题何时不再继续分解,即最简单、可直接

c语言函数递归 怎么选?常见方案对比分析
c语言函数递归 怎么选?常见方案对比分析

递归函数的基本概念与适用场景在C语言编程中,递归是一种函数调用自身的编程技巧。它并非适用于所有问题,但在处理某些具有自相似结构的问题时,能提供极其清晰和优雅的解决方案。递归的核心思想是将一个大规模问题分解为一个或多个同类型但规模更小的子问题,直到子问题简单到可以直接求解。典型的适用场景包括树形结构的

Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解
Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解

理解内存管理的基石在Objective-C的编程世界中,内存管理是开发者必须掌握的核心技能之一。它直接关系到应用的性能、稳定性与资源利用效率。与一些采用自动垃圾回收机制的语言不同,Objective-C在很长一段时间里,依赖一套基于引用计数的、需要开发者部分介入的管理规则。这套规则的核心思想是明确的

如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏
如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏

理解 dealloc 的角色与时机在 iOS 应用开发中,内存管理是保障应用性能与稳定性的基石。dealloc 方法是 Objective-C 中对象生命周期结束时的关键回调,它标志着对象即将被系统回收内存。正确理解其触发时机至关重要:当一个对象的引用计数降为零时,运行时系统会自动调用该对象的 de

深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制
深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制

内存管理的基石在Objective-C的世界里,内存管理是开发者必须掌握的核心技能之一。作为一门在手动引用计数(MRC)时代诞生的语言,Objective-C要求程序员对对象的生命周期有清晰的认识。dealloc方法正是这一生命周期中至关重要的终点站。它是一个实例方法,当对象的引用计数降为零时,系统

理解 native2ascii:Java 国际化开发中的字符编码工具
理解 native2ascii:Java 国际化开发中的字符编码工具

native2ascii 工具的基本定位在Ja va应用程序的国际化与本地化开发过程中,处理非拉丁字符集是一个常见且关键的环节。Ja va内部使用Unicode字符集来统一表示全球各种语言的文字,但其属性文件(.properties)在历史上要求使用ASCII编码,或者更准确地说,要求非ASCII字

如何使用 native2ascii 转换中文字符为 Unicode 转义序列
如何使用 native2ascii 转换中文字符为 Unicode 转义序列

理解 native2ascii 工具的基本用途在软件开发,特别是涉及国际化处理的场景中,开发者常常需要处理不同编码的文本资源。native2ascii 是 Ja va 开发工具包(JDK)中提供的一个命令行实用程序,其主要功能是将包含本地字符编码(非ASCII字符)的文件,转换为包含 Unicode

Java native2ascii 命令详解:解决属性文件乱码问题
Java native2ascii 命令详解:解决属性文件乱码问题

native2ascii 命令的由来与作用在Ja va开发中,处理国际化资源文件是一个常见需求。资源文件通常以.properties格式存储,用于支持多语言界面。然而,Ja va属性文件默认采用ISO-8859-1字符集编码,这导致了一个直接的问题:当文件中包含非拉丁字符(如中文、日文、韩文等)时,

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

macOS软件
macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

Mac软件 更多
灵活计算器
灵活计算器
macOS/iOS/Android

灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

赤友清理大师
赤友清理大师
macOS

赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

WINDOWS 更多
Windows 10
Windows 10
Windows

Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

密码键盘
密码键盘
Windows/macOS/iOS/Android

密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。