当前位置:

首页 > 编程开发 > C++之高性能跨平台日志库spdlog

C++之高性能跨平台日志库spdlog

spdlog是C++高性能日志库,异步模式可达每秒百万条消息。采用无锁队列、环形队列与零拷贝设计,支持同步/异步双模式。核心模块包括Logger、Sink、Formatter、Registry、线程池及MPMC队列,通过预分配与模板策略实现极致性能。

本文目录
    spdlog 是目前 C++ 社区里相当热门的一个日志框架,核心卖点就是快——快到什么程度?异步模式下每秒百万条消息的吞吐量,比 glog、log4cpp 快了不止一个量级。 它的几个关键特性值得一说:异步模式采用无锁队列配合线程池,主线程把日志消息丢进队列就返回,几乎不产生阻塞;预分配的环形队列和零拷贝设计,在高并发场景下内存分配压力极低;支持同步/异步双模式切换,同步模式直接写 I/O,简单直接,异步模式后台线程统一处理,业务逻辑完全不卡。

    核心流程

    核心模块

    要理解 spdlog 的设计,先看它的几个核心模块各自扮演什么角色:
    模块核心职责/作用输入/输出
    Logger日志级别过滤、消息分发、错误处理,统一入口,支持多 sink 组合格式化字符串 + 参数,输出 log_msg 对象
    Sink实际 I/O 操作、格式化输出,可扩展输出目标,单一职责输入 log_msg 对象,输出字节流到目标
    Formatter消息格式化、时间戳处理、颜色标记,支持自定义格式,缓存优化输入 log_msg 对象,输出格式化字符串
    Registry全局 logger 管理、配置分发、生命周期,集中管理,避免全局变量污染输入 logger 注册请求,输出 logger 引用
    Thread Pool异步消息处理、后台线程调度,解耦业务线程与 I/O 线程输入 async_msg 对象,调用 logger sink_it_
    MPMC Queue线程安全消息队列、阻塞/非阻塞策略,生产者-消费者解耦,零分配输入 log_msg 对象,输出出队消息
    从应用角度来看,不同场景下各模块的适用性也有所不同:
    能力适用场景不适用场景
    同步日志单线程应用、调试阶段、低频日志高并发生产环境、性能敏感路径
    异步日志高并发服务、游戏引擎、实时系统需要严格顺序保证、崩溃时日志不能丢失
    环形缓冲固定内存预算、可容忍消息丢失需要持久化所有日志、审计场景
    多 Sink 组合同时输出到文件/控制台/网络单一输出目标、极简场景
    自定义格式结构化日志、日志分析系统标准格式即可满足需求

    核心执行时序

    同步日志执行流程

    异步日志执行流程

    原理与设计

    spdlog 能在保持接口简洁的同时实现极致性能,靠的是预分配与零拷贝设计这套组合拳: 1. 环形队列:牺牲部分灵活性(固定大小),换来零分配与缓存友好 2. 模板策略:在编译期决定线程安全策略,运行时零开销 3. 异步解耦:业务线程只负责入队,I/O 延迟被隔离在后台,不干扰核心路径

    关键抽象与机制

    环形队列(Circular Queue)

    环形队列是 spdlog 性能优化的基石,实现位于 `circular_q.h`: ```cpp template class circular_q { size_t max_items_ = 0; typename std::vector::size_type head_ = 0; typename std::vector::size_type tail_ = 0; size_t overrun_counter_ = 0; std::vector v_; void push_back(T &&item) { if (max_items_ > 0) { v_[tail_] = std::move(item); tail_ = (tail_ + 1) % max_items_; if (tail_ == head_) { // 队列满,覆盖最旧消息 head_ = (head_ + 1) % max_items_; ++overrun_counter_; } } } }; ``` 几个关键设计点值得注意: - 预分配:构造时一次性分配 `max_items + 1` 个元素,运行时不做任何内存分配 - 覆盖策略:队列满了怎么办?直接覆盖最旧的消息,保证写入操作永不阻塞 - 零拷贝:`std::move` 转移所有权,避免深拷贝带来的性能损耗 - 计数器:`overrun_counter_` 记录被覆盖的消息数量,方便监控

    MPMC 阻塞队列

    多生产者-多消费者队列位于 `mpmc_blocking_q.h`,在环形队列基础上封装了线程安全机制: ```cpp template class mpmc_blocking_queue { std::mutex queue_mutex_; std::condition_variable push_cv_; std::condition_variable pop_cv_; spdlog::details::circular_q q_; std::atomic discard_counter_{0}; void enqueue(T &&item) { { std::unique_lock lock(queue_mutex_); pop_cv_.wait(lock, [this] { return !this->q_.full(); }); q_.push_back(std::move(item)); } push_cv_.notify_one(); } bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) { { std::unique_lock lock(queue_mutex_); if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); })) { return false; } popped_item = std::move(q_.front()); q_.pop_front(); } pop_cv_.notify_one(); return true; } }; ``` 并发控制方面有几个亮点: - 双条件变量:`push_cv_` 负责唤醒消费者线程,`pop_cv_` 唤醒生产者线程,避免盲目轮询 - 细粒度锁:锁的持有时间只覆盖队列操作,不包含 I/O 等耗时步骤 - 超时机制:`dequeue_for` 支持超时返回,防止死锁 - 丢弃计数:`discard_counter_` 记录 `enqueue_if_ha ve_room` 失败的次数

    Sink 抽象与线程安全

    Sink 接口定义在 `sink.h`: ```cpp class sink { public: virtual ~sink() = default; virtual void log(const details::log_msg &msg) = 0; virtual void flush() = 0; virtual void set_pattern(const std::string &pattern) = 0; virtual void set_formatter(std::unique_ptr sink_formatter) = 0; void set_level(level::level_enum log_level); level::level_enum level() const; bool should_log(level::level_enum msg_level) const; protected: level_t level_{level::trace}; }; ``` 线程安全是通过模板策略实现的,位于 `base_sink.h`: ```cpp template class base_sink : public sink { public: void log(const details::log_msg &msg) final override { std::lock_guard lock(mutex_); sink_it_(msg); } void flush() final override { std::lock_guard lock(mutex_); flush_(); } protected: std::unique_ptr formatter_; Mutex mutex_; virtual void sink_it_(const details::log_msg &msg) = 0; virtual void flush_() = 0; }; ``` 这个设计有几个明显优势: - 编译期多态:通过模板参数选择 `std::mutex` 或者 `details::null_mutex` - 零运行时开销:单线程场景下使用 `null_mutex`,锁操作被编译器完全优化掉 - CRTP 模式:基类控制整体流程,派生类只需要实现具体的 I/O 逻辑

    格式化器与时间戳缓存

    Pattern Formatter 位于 `pattern_formatter.h`: ```cpp class pattern_formatter final : public formatter { private: std::string pattern_; std::string eol_; pattern_time_type pattern_time_type_; bool need_localtime_; std::tm cached_tm_; std::chrono::seconds last_log_secs_; std::vector> formatters_; std::tm get_time_(const details::log_msg &msg) { if (need_localtime_) { auto time_now = log_clock::to_time_t(msg.time); if (last_log_secs_ != time_now) { cached_tm_ = localtime(time_now); last_log_secs_ = time_now; } return cached_tm_; } return gmtime(log_clock::to_time_t(msg.time)); } }; ``` 性能优化的几个关键点: - 时间戳缓存:同一秒内的日志共享同一个 `std::tm` 结构,避免重复转换 - 编译期解析:模式字符串在构造时就解析为 `flag_formatter` 链表,运行时无需解析 - 内存复用:`memory_buf_t` 使用 fmt 的 `memory_buffer`,避免反复分配内存

    核心设计取舍

    性能 vs 易用性

    设计决策性能收益易用性代价适用场景
    Header-only 模式无编译优化,编译时间长无需构建,集成简单快速原型、小型项目
    编译模式编译时间短,二进制体积小需要 CMake 配置生产环境、大型项目
    异步模式业务线程零阻塞崩溃时可能丢失日志高并发服务
    同步模式日志顺序严格保证I/O 阻塞业务线程调试、审计场景
    一个值得注意的权衡是:异步模式下,日志消息在入队时已经完成了格式化,但 `log_msg_buffer` 仍然需要拷贝 payload。这么做的目的是确保业务线程释放原始字符串后,后台线程仍然能安全访问日志内容。

    一致性 vs 可用性

    ```cpp enum class async_overflow_policy { block, // 阻塞直到有空间(强一致性) overrun_oldest, // 覆盖最旧消息(高可用性) discard_new // 丢弃新消息(低延迟优先) }; ``` 不同场景下该选哪种策略? - block:审计日志的首选,不能容忍任何丢失,代价是可能阻塞业务线程 - overrun_oldest:监控日志的好选择,能容忍旧数据丢失,保证实时性 - discard_new:调试日志的理想方案,队列满了直接丢弃新消息,避免阻塞

    抽象程度 vs 灵活性

    Sink 的继承层级相当清晰: ``` sink (接口) └─ base_sink (模板基类,提供线程安全) ├─ basic_file_sink (单文件) ├─ rotating_file_sink (滚动文件) ├─ daily_file_sink (按日期分割) └─ stdout_sink (控制台) ``` 扩展机制也很灵活: - 自定义 Sink:继承 `base_sink`,实现 `sink_it_()` 和 `flush_()` 即可 - 自定义 Formatter:继承 `custom_flag_formatter` 并注册到 `pattern_formatter` - 自定义错误处理:通过 `set_error_handler()` 注册回调函数

    源码地图

    ``` spdlog-1.15.2/ ├── include/spdlog/ │ ├── spdlog.h # 全局 API 入口,registry 访问 │ ├── logger.h # 核心 logger 类,同步日志实现 │ ├── async_logger.h # 异步 logger,继承 logger │ ├── formatter.h # 格式化器接口 │ ├── pattern_formatter.h # 默认格式化器实现 │ ├── async.h # 异步工厂函数 │ ├── common.h # 公共类型定义,编译配置 │ │ │ ├── details/ │ │ ├── registry.h # 全局 logger 注册表 │ │ ├── thread_pool.h # 异步线程池 │ │ ├── mpmc_blocking_q.h # 多生产者多消费者队列 │ │ ├── circular_q.h # 环形队列底层实现 │ │ ├── log_msg.h # 日志消息结构 │ │ ├── log_msg_buffer.h # 带缓冲的消息,用于异步 │ │ └── file_helper.h # 文件操作辅助类 │ │ │ └── sinks/ │ ├── sink.h # Sink 接口定义 │ ├── base_sink.h # 线程安全模板基类 │ ├── basic_file_sink.h # 基础文件 Sink │ ├── rotating_file_sink.h # 滚动文件 Sink │ ├── daily_file_sink.h # 日期分割 Sink │ ├── stdout_sinks.h # 控制台 Sink │ └── dist_sink.h # 分发 Sink(多目标) │ └── src/ ├── spdlog.cpp # 编译模式实现 ├── async.cpp # 异步相关实现 └── bundled_fmtlib_format.cpp # fmt 库编译实现 ``` 几个核心文件需要重点关注: 1. logger.h:定义了 `logger` 类,涵盖日志级别过滤、错误处理、sink 管理 2. async_logger.h:重写了 `sink_it_()` 和 `flush_()`,负责把消息推送到线程池 3. mpmc_blocking_q.h:核心并发数据结构,直接决定异步性能的上限 4. pattern_formatter.h:格式化逻辑,时间戳缓存优化是性能关键 5. registry.h:全局状态管理,logger 的整个生命周期都靠它掌控

    API 使用

    常用 API

    全局 API

    通过 `spdlog::` 命名空间可以直接访问:
    API参数说明功能说明
    spdlog::info(fmt, args...)fmt: 格式化字符串,args: 参数使用默认 logger 输出 INFO 级别日志
    spdlog::set_level(level)level: level::trace 到 level::off设置全局日志级别
    spdlog::set_pattern(pattern)pattern: 格式模式字符串设置全局格式模式
    spdlog::get(name)name: logger 名称获取已注册的 logger,不存在返回 nullptr
    spdlog::drop(name)name: logger 名称从 registry 移除 logger
    spdlog::shutdown()无停止所有线程,清理资源
    spdlog::flush_on(level)level: 触发 flush 的级别设置自动 flush 级别
    spdlog::flush_every(interval)interval: 时间间隔启动周期性 flush 线程

    Logger API

    API参数说明功能说明
    logger->log(level, fmt, args...)level: 日志级别输出指定级别日志
    logger->set_level(level)level: 日志级别设置该 logger 的日志级别
    logger->flush()无手动 flush 所有 sink
    logger->sinks()无返回 sink 列表引用,可动态添加 sink
    logger->set_formatter(formatter)formatter: 格式化器指针设置自定义格式化器
    logger->error_handler()无获取当前错误处理器
    logger->set_error_handler(handler)handler: 错误处理函数设置错误处理回调

    Sink API

    API参数说明功能说明
    sink->set_level(level)level: 日志级别设置 sink 级别过滤
    sink->set_pattern(pattern)pattern: 格式模式设置 sink 格式
    sink->flush()无flush 该 sink

    异步 API

    API参数说明功能说明
    spdlog::create_async(name, args...)Sink: sink 类型,name: logger 名称创建异步 logger
    spdlog::init_thread_pool(q_size, n_threads)q_size: 队列大小,n_threads: 线程数初始化全局线程池
    async_logger(logger_name, sink, tp, policy)tp: 线程池,policy: 溢出策略构造异步 logger

    样例 Demo

    下面是一个完整的示例,涵盖了错误处理、多 sink、异步模式和资源清理: ```cpp #include #include #include #include #include #include #include class GameLogger { public: static bool Initialize() { try { // 初始化线程池,队列大小8192,工作线程数1 spdlog::init_thread_pool(8192, 1); // 创建控制台 sink(彩色输出) auto console_sink = std::make_shared(); console_sink->set_level(spdlog::level::debug); console_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [%t] %v"); // 创建滚动文件 sink(单个文件5MB,保留3个备份) auto file_sink = std::make_shared( "logs/game.log", 1024 * 1024 * 5, 3); file_sink->set_level(spdlog::level::info); file_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] [%t] [%s:%#] %v"); // 组合多个 sink std::vector sinks{console_sink, file_sink}; // 创建异步 logger auto logger = std::make_shared( "game_logger", sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block ); logger->set_level(spdlog::level::debug); logger->flush_on(spdlog::level::warn); // 设置错误处理器 logger->set_error_handler([](const std::string &msg) { std::cerr << "Logger error: " << msg << std::endl; }); // 注册为默认 logger spdlog::register_logger(logger); spdlog::set_default_logger(logger); // 启用回溯,存储最近32条日志 spdlog::enable_backtrace(32); SPDLOG_INFO("GameLogger initialized successfully"); return true; } catch (const spdlog::spdlog_ex &ex) { std::cerr << "Logger initialization failed: " << ex.what() << std::endl; return false; } } static void Shutdown() { SPDLOG_INFO("Shutting down GameLogger"); // 如果有错误,dumo 回溯信息 spdlog::dump_backtrace(); // flush 所有 logger spdlog::apply_all([](std::shared_ptr l) { l->flush(); }); // 释放所有 logger 并停止线程 spdlog::shutdown(); } static void LogGameEvent(const std::string &event_name, int player_id, float value) { SPDLOG_INFO("GameEvent: {} [player={}, value={:.2f}]", event_name, player_id, value); } static void LogPerformanceMetric(const std::string &metric, double ms) { if (ms > 16.67) { // 超过60FPS的帧时间阈值 SPDLOG_WARN("Performance warning: {} took {:.2f}ms", metric, ms); } else { SPDLOG_DEBUG("Performance: {} = {:.2f}ms", metric, ms); } } }; int main() { if (!GameLogger::Initialize()) { return 1; } try { // 基础日志 SPDLOG_INFO("Game started"); SPDLOG_DEBUG("Debug message (only visible in debug builds)"); // 格式化日志 GameLogger::LogGameEvent("PlayerJump", 12345, 98.5f); GameLogger::LogPerformanceMetric("RenderFrame", 14.2); GameLogger::LogPerformanceMetric("PhysicsUpdate", 18.5); // 错误处理 SPDLOG_ERROR("Simulated error with code {}", 404); // 手动 flush spdlog::default_logger()->flush(); } catch (const std::exception &ex) { SPDLOG_CRITICAL("Exception: {}", ex.what()); GameLogger::Shutdown(); return 1; } GameLogger::Shutdown(); return 0; } ``` 这个示例体现了几条工程实践: 1. 初始化顺序:先创建线程池,再创建 logger,最后注册 2. 错误处理:捕获 `spdlog::spdlog_ex` 异常,设置错误处理器 3. 资源清理:`shutdown()` 必须调用,否则异步线程不会主动停止 4. Flush 策略:`flush_on(warn)` 确保 warning 及以上级别立即写入 5. Backtrace:存储最近 N 条日志,崩溃时 dump 出来用于调试

    场景建议

    配置管理

    ```cpp // config/logger_config.h struct LoggerConfig { std::string log_file_path = "logs/app.log"; size_t max_file_size = 5 * 1024 * 1024; // 5MB int max_files = 3; spdlog::level::level_enum level = spdlog::level::info; bool async_mode = true; size_t queue_size = 8192; std::string pattern = "[%Y-%m-%d %H:%M:%S.%e] [%l] [%t] %v"; static LoggerConfig LoadFromFile(const std::string &config_path); }; ```

    日志追踪

    ```cpp // 使用 MDC(Mapped Diagnostic Context)进行请求追踪 #include void HandleHttpRequest(const HttpRequest &req) { // 为当前作用域内的所有日志设置 trace ID spdlog::mdc::put("trace_id", req.trace_id()); spdlog::mdc::put("user_id", std::to_string(req.user_id())); SPDLOG_INFO("Processing request"); // ... 业务逻辑 ... spdlog::mdc::remove("trace_id"); spdlog::mdc::remove("user_id"); } // 配合 MDC 的格式化模式 // Pattern: [%Y-%m-%d %H:%M:%S.%e] [%l] [trace:%X{trace_id}] %v ```

    性能调优

    ```cpp // 监控异步队列健康状态 void MonitorLoggerQueue() { auto tp = spdlog::thread_pool(); if (tp) { size_t overrun = tp->overrun_counter(); size_t discard = tp->discard_counter(); size_t queue_size = tp->queue_size(); if (overrun > 0 || discard > 0) { SPDLOG_WARN("Logger queue issues: overrun={}, discard={}, size={}", overrun, discard, queue_size); } // 监控完后重置计数器 tp->reset_overrun_counter(); tp->reset_discard_counter(); } } // 编译期日志级别控制,release 版本屏蔽 debug 日志 #ifndef NDEBUG #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG #else #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO #endif // 使用宏代替函数调用,编译期自动过滤 SPDLOG_INFO("This call may be optimized out in release"); ```

    监控集成

    ```cpp // 自定义 sink 收集指标 class MetricsSink : public spdlog::sinks::base_sink { protected: void sink_it_(const spdlog::details::log_msg &msg) override { // 增加指标计数器 metrics::LogCounter(msg.level).Increment(); // 追踪错误率 if (msg.level >= spdlog::level::err) { metrics::ErrorRate.Mark(); } } void flush_() override { // flush 指标到后端 metrics::Flush(); } }; // 注册指标 sink auto metrics_sink = std::make_shared(); spdlog::default_logger()->sinks().push_back(metrics_sink);
    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    悟空浏览器怎么彻底清理浏览器配置文件
    悟空浏览器怎么彻底清理浏览器配置文件

    彻底清理悟空浏览器的数据,通常有三个路径:通过手机系统设置能一次性清除所有配置和用户信息;利用浏览器内置功能,可以只清理缓存而保留个性化设置;最彻底的则是卸载重装,让应用回到初始状态。 悟空浏览器用久了,是不是偶尔会感觉变慢,或者出现一些难以解释的小毛病?有时候,与其一个个排查问题,不如来一次彻底的

    漫蛙漫画官网首页登录入口
    漫蛙漫画官网首页登录入口

    漫蛙漫画首页登录入口https://www.manwamh.top/:一触即达的漫画世界核心枢纽 对于漫画迷而言,找到那个可靠的资源门户至关重要。漫蛙漫画的首页登录入口,正是这样一个核心渠道。通过它,你可以直接跳转到漫蛙2漫画APP的下载页面,轻松几步完成安装,马上就能投入到精彩纷呈的漫画之旅中。 漫蛙漫画首页登录入口: 官方入口地址已经为你备好

    漫蛙漫画官方入口
    漫蛙漫画官方入口

    漫蛙漫画官方入口与核心功能解析 寻找一个稳定、资源丰富的漫画在线阅读平台,是许多漫画爱好者的共同需求。其中,漫蛙漫画官方入口及其免费全集服务,受到了不少关注。那么,这个平台究竟如何?下面,我们就来深入了解一下它的具体信息与特色功能。 平台的官方访问地址是:https://www.manwa123.c

    漫蛙动漫APP最新版本下载
    漫蛙动漫APP最新版本下载

    对于动漫爱好者而言,找到一个资源全、体验佳的追番平台,常常是件头疼事。漫蛙动漫APP的定位很明确:就是要打造一个聚合全球正版动漫资源的专属乐园。无论是当季火爆的日漫新番、值得回味的经典国创,还是那些小众却精彩的优质动画,在这里基本都能一网打尽。平台底层采用了智能缓冲技术,最大程度保证了播放的流畅度,

    漫蛙官网入口免费下载最新版
    漫蛙官网入口免费下载最新版

    想要获取正版manwa漫蛙APP?官方指定下载渠道就是漫蛙官网入口。 对于漫画迷而言,找到一款心仪的阅读神器至关重要。漫蛙APP正是这样一款专为全球漫画爱好者打造的平台,它汇聚了从日韩人气连载到本土原创精品的海量资源。说它“应有尽有”毫不为过。更重要的是,其界面设计清爽直观,搭配智能检索功能,能让用

    漫蛙漫画在线观看入口
    漫蛙漫画在线观看入口

    想要畅享海量正版漫画资源?漫蛙漫画平台不容错过 对于漫画迷来说,找到一个资源丰富、体验流畅的正版平台实属幸事。漫蛙漫画凭借其优质的在线阅读服务,已经成为了许多资深读者的心头好。今天,我们就来详细拆解一下平台的访问方法与实用技巧,助你轻松开启漫画之旅。 漫蛙漫画网页入口页面指南 想立刻体验?官方在线观

    漫蛙如何下载最新版本
    漫蛙如何下载最新版本

    漫蛙漫画网APP官方下载方法全解析 对于资深漫画迷来说,找到一个资源全、更新快、体验佳的阅读平台,就像淘到了宝藏。漫蛙漫画网APP,正是这样一个专为爱好者打造的精品集散地。它汇聚了全球优质的正版漫画,从火爆的日漫连载到崛起的国创佳作,平台都能做到实时同步更新。当然,第一步得先把它装到设备上。接下来,

    漫蛙漫画APP最新版本下载
    漫蛙漫画APP最新版本下载

    漫蛙漫画APP:专业漫画阅读工具的全面革新 作为一款专业的漫画阅读工具,漫蛙漫画APP整合了全球优质漫画资源,其升级版漫蛙2在功能与体验上可谓全面革新。这款应用不仅囊括日漫经典与国漫新作,更通过简洁直观的界面设计和流畅操作,为用户打造沉浸式阅读环境,充分满足各类漫画爱好者的多样化需求。 漫蛙漫画特色

    卖茶叶妈妈3d漫蛙安卓版下载
    卖茶叶妈妈3d漫蛙安卓版下载

    当下动漫圈热议的卖茶叶妈妈3d漫蛙下载,凭借其突破性的3D视觉技术成为焦点 最近动漫爱好者们都在讨论一个现象级产品——卖茶叶妈妈3d漫蛙下载。这款由漫蛙漫画平台倾力打造的3D作品专区,用立体建模与动态分镜彻底颠覆了传统二维漫画的阅读模式。你猜怎么着?现在只需通过专区提供的便捷下载通道,无论是热血冒险

    漫蛙下载最新版本
    漫蛙下载最新版本

    漫蛙APP:资深漫画迷的移动图书馆与阅读利器 对于漫画爱好者来说,找到一个资源全、体验好的阅读平台,从来都不是件容易的事。而漫蛙APP,恰恰就是为这个需求而生的。它凭借海量的正版漫画库和极其便捷的离线下载功能,在圈内积累了相当不错的口碑。更重要的是,从经典的日本热血漫到新兴的国产原创作品,它几乎一网

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

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

    Windows
    Windows

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

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。