从入门到精通:C++线程池实现全流程解析(含完整源码)

从入门到精通:20+ 优质 C++ 开源项目推荐(附学习指南) 本文为C++开发者精选了20+个高质量开源项目,按难度分为入门级(500-3000行)、进阶级(5000-20000行)和专业级(20000+行)三类。推荐项目包括TinyHttpd、muduo、LevelDB等经典案例,涵盖网络编程、存储系统、现代C++特性等方向。文章强调通过阅读优秀代码学习工程实践,建议开发者循序渐进学习,带着问题阅读代码,动手实践并做好总结。同时提供了游戏开发、工具类等特色领域项目推荐,帮助开发者从基础到进阶系统提升C++能力。 阅读详情

第一章:C++线程池概述与核心概念

线程池是一种用于管理和复用线程的机制,广泛应用于高并发服务器、异步任务处理等场景。它通过预先创建一组工作线程,避免频繁创建和销毁线程带来的性能开销,从而提升系统的响应速度和资源利用率。

线程池的基本组成

一个典型的C++线程池通常包含以下几个核心组件:
  • 任务队列:用于存放待执行的任务,通常为线程安全的队列。
  • 线程集合:一组预先启动的工作线程,持续从任务队列中取出任务并执行。
  • 调度机制:控制任务的提交、分配与执行流程。
  • 同步原语:如互斥锁(mutex)和条件变量(condition_variable),保障多线程环境下的数据安全。

典型线程池的工作流程

步骤描述
1初始化指定数量的工作线程
2线程进入等待状态,监听任务队列
3外部提交任务到任务队列
4唤醒一个线程执行任务
5任务执行完毕后线程返回等待状态

简单任务提交示例

以下是一个基于 lambda 表达式提交任务的伪代码结构,展示了如何向线程池添加可调用对象:

// 定义任务类型:可调用对象,无参数无返回值
using Task = std::function;

// 向任务队列添加任务(需加锁保护)
void submit(Task task) {
    std::unique_lock<std::mutex> lock(queue_mutex);
    tasks.push(task);
    condition.notify_one(); // 唤醒一个工作线程
}
graph TD A[提交任务] --> B{任务队列是否为空?} B -->|否| C[唤醒工作线程] B -->|是| D[继续等待新任务] C --> E[线程执行任务] E --> F[任务完成,返回等待]

第二章:线程池设计原理与关键技术

2.1 线程池的基本结构与工作流程

线程池由核心线程数、最大线程数、任务队列和拒绝策略四部分构成,其工作流程遵循“提交任务→分配执行→回收资源”的模式。
核心组件说明
  • 核心线程数(corePoolSize):长期保留的线程数量
  • 最大线程数(maximumPoolSize):允许创建的最大线程上限
  • 任务队列(workQueue):缓存待处理任务的阻塞队列
  • 拒绝策略(RejectedExecutionHandler):队列满且线程达上限时的处理机制
典型执行流程示例
ExecutorService executor = new ThreadPoolExecutor(
    2,                    // corePoolSize
    4,                    // maximumPoolSize
    60L,                  // keepAliveTime
    TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(10) // workQueue
);
上述代码定义了一个可扩展的线程池:当任务数 ≤ 2,复用核心线程;超过时临时创建新线程至最大4个,超出队列容量则触发拒绝策略。

2.2 任务队列的设计与线程安全实现

在高并发系统中,任务队列是解耦生产者与消费者的关键组件。为确保多线程环境下数据一致性,必须采用线程安全机制。
线程安全队列的核心设计
使用互斥锁(Mutex)保护共享队列的读写操作,防止竞态条件。以下为Go语言实现示例:
type TaskQueue struct {
    tasks   []*Task
    mutex   sync.Mutex
    cond    *sync.Cond
}

func (q *TaskQueue) Push(task *Task) {
    q.mutex.Lock()
    defer q.mutex.Unlock()
    q.tasks = append(q.tasks, task)
    q.cond.Signal() // 唤醒等待的消费者
}
上述代码中,mutex确保同一时间只有一个线程可修改队列;sync.Cond用于阻塞消费者线程直至新任务到达,提升效率。
关键同步机制对比
机制适用场景性能特点
互斥锁 + 条件变量高频读写均衡低延迟,支持等待通知
原子操作简单计数或状态标记高性能但功能受限

2.3 线程生命周期管理与资源回收

线程的生命周期涵盖创建、运行、阻塞、终止等阶段,精准管理各状态转换是避免资源泄漏的关键。
线程状态转换机制
操作系统通过调度器控制线程状态迁移。典型状态包括就绪、运行、等待和终止。开发者需显式调用接口完成资源释放。
资源回收示例(Go语言)
go func() {
    defer wg.Done() // 确保任务完成后通知
    // 执行业务逻辑
}()
上述代码中,wg.Done() 在协程退出前被调用,防止主程序过早退出导致资源未回收。使用 defer 可确保无论函数因何原因返回,清理操作均被执行。
  • 创建:分配栈空间与TCB(线程控制块)
  • 终止:调用 pthread_join 回收内核资源

2.4 条件变量与互斥锁的协同机制

同步原语的协作基础
条件变量用于线程间通信,通常与互斥锁配合使用,以实现对共享资源的安全访问和等待唤醒机制。互斥锁保护临界区,而条件变量允许线程在特定条件不满足时挂起。
典型使用模式
线程在检查条件前必须先获取互斥锁,若条件不成立,则调用 wait() 原子地释放锁并进入阻塞状态。当其他线程修改状态后,通过 signal() 或 broadcast() 通知等待线程。
var mu sync.Mutex
var cond = sync.NewCond(&mu)
var ready bool

func waitForReady() {
    mu.Lock()
    for !ready {
        cond.Wait() // 释放锁并等待
    }
    fmt.Println("Ready is true, proceeding...")
    mu.Unlock()
}

func setReady() {
    mu.Lock()
    ready = true
    cond.Signal() // 唤醒一个等待者
    mu.Unlock()
}
上述代码中,cond.Wait() 内部会自动释放 mu,并在被唤醒后重新加锁,确保条件判断与等待操作的原子性。这种协同机制避免了竞争条件,是构建高效并发模型的核心手段之一。

2.5 高并发场景下的性能优化策略

缓存策略优化
在高并发系统中,合理使用缓存可显著降低数据库压力。采用本地缓存(如Guava Cache)与分布式缓存(如Redis)结合的方式,能有效提升响应速度。
// 示例:使用Redis实现请求缓存
func getCachedData(key string) (string, error) {
    val, err := redisClient.Get(context.Background(), key).Result()
    if err != nil {
        return fetchFromDB(key) // 缓存未命中则查库
    }
    return val, nil
}
上述代码通过优先查询Redis缓存,避免频繁访问数据库,减少响应延迟。key为请求唯一标识,redisClient为预初始化的客户端实例。
连接池配置
使用数据库连接池(如HikariCP、GORM连接池)控制最大连接数,防止资源耗尽。建议设置:
  • 最大连接数:根据数据库承载能力设定,通常为CPU核心数的2-4倍
  • 空闲超时时间:避免长期占用无用连接
  • 启用连接健康检查机制

第三章:线程池核心组件实现

3.1 任务类与可调用对象的封装

在并发编程中,任务的抽象是构建高效执行框架的基础。将任务封装为可调用对象,能够统一调度接口,提升代码复用性。
任务类的设计模式
通过定义通用接口,任务类可实现一致的执行契约。例如在Go语言中:
type Task interface {
    Call() error
}
该接口允许任何实现类型作为可调用任务提交至执行器,Call方法封装具体业务逻辑。
可调用对象的封装优势
  • 解耦任务定义与执行时机
  • 支持异步执行与结果回调
  • 便于资源管理与错误传播
结合闭包封装,可进一步简化任务构造:
func NewTask(f func() error) Task {
    return &task{fn: f}
}
此模式将函数包装为Task实例,提升灵活性与内聚性。

3.2 线程管理模块的编码实践

在构建高并发系统时,线程管理模块的设计直接影响系统的稳定性和性能。合理的线程创建、调度与销毁机制是保障服务响应能力的关键。
线程池的初始化配置
使用线程池可有效控制并发线程数量,避免资源耗尽。以下为基于 Java 的线程池配置示例:

ExecutorService threadPool = new ThreadPoolExecutor(
    4,                    // 核心线程数
    10,                   // 最大线程数
    60L,                  // 空闲线程存活时间
    TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(100), // 任务队列容量
    new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
该配置中,核心线程保持常驻,任务超队列后创建新线程直至最大值,超出则由调用线程执行任务,防止 abrupt 拒绝。
线程生命周期监控
  • 通过 Future 对象跟踪任务执行状态
  • 注册 Shutdown Hook 处理 JVM 关闭前的线程优雅退出
  • 使用 ThreadLocal 注意内存泄漏,务必在 finally 块中清理

3.3 完整线程池类的构建与接口设计

为了实现高效的并发任务调度,线程池的核心类需封装工作线程管理、任务队列和生命周期控制。
核心接口设计
线程池对外暴露统一接口,包括任务提交、关闭和状态查询:
  • Submit(task func()):向任务队列投递闭包函数
  • Shutdown():优雅关闭,等待运行中任务完成
  • Running() int:返回当前活跃线程数
结构体定义与初始化
type ThreadPool struct {
    workers    chan *worker
    tasks      chan func()
    closeOnce  sync.Once
    wg         sync.WaitGroup
}

func NewThreadPool(workerCount int) *ThreadPool {
    pool := &ThreadPool{
        workers: make(chan *worker, workerCount),
        tasks:   make(chan func(), 1024),
    }
    for i := 0; i < workerCount; i++ {
        w := newWorker(pool.tasks)
        pool.workers <- w
        pool.wg.Add(1)
        go w.start(&pool.wg)
    }
    return pool
}
该构造函数预创建指定数量的工作协程,通过无缓冲通道接收任务,sync.WaitGroup确保优雅退出时等待所有任务完成。

第四章:功能扩展与实际应用

4.1 支持返回值的任务提交机制

在并发编程中,任务执行后获取结果是常见需求。传统的 `Runnable` 接口无法返回执行结果,为此 Java 提供了 `Callable` 接口,允许任务执行完成后返回指定类型的值。
Callable 与 Future 配合使用
通过 `ExecutorService` 提交 `Callable` 任务,返回一个 `Future` 对象,用于获取任务结果或检查执行状态。

Callable<Integer> task = () -> {
    Thread.sleep(1000);
    return 42;
};
Future<Integer> future = executor.submit(task);
Integer result = future.get(); // 阻塞直至结果可用
上述代码定义了一个返回整数的异步任务。`submit` 方法提交任务后立即返回 `Future` 实例。调用 `get()` 会阻塞当前线程,直到任务完成并返回结果 42。
核心方法说明
  • submit(Callable<T>):提交有返回值的任务
  • future.get():获取任务结果,可能阻塞
  • future.isDone():判断任务是否完成

4.2 线程池的动态扩容与收缩

线程池的动态扩容与收缩机制是提升系统资源利用率和响应性能的关键策略。通过运行时调整核心线程数、最大线程数及空闲线程回收策略,可适应负载变化。
动态扩容触发条件
当任务队列满且当前线程数小于最大线程数时,线程池会创建新线程处理任务:
  • 任务提交速率突增
  • 核心线程已满,队列饱和
  • 允许创建超过核心线程的临时线程
自动收缩实现方式
通过设置 allowCoreThreadTimeOut 参数,使核心线程也能被回收:
threadPool.allowCoreThreadTimeOut(true);
threadPool.setKeepAliveTime(60, TimeUnit.SECONDS);
上述代码表示所有空闲线程(包括核心线程)在60秒无任务后将被终止,从而实现自动收缩。
参数配置对比
参数默认值动态调节建议
corePoolSize1根据常驻并发量设定
maximumPoolSizeInteger.MAX_VALUE结合CPU与内存限制调整

4.3 异常处理与线程安全的日志记录

在高并发系统中,日志记录不仅需要保证异常信息的完整性,还必须确保多线程环境下的安全性。
异常捕获与结构化输出
使用结构化日志格式(如JSON)可提升日志可解析性。以下为Go语言示例:
logger := log.New(os.Stdout, "", log.LstdFlags)
mu := &sync.Mutex{}

func SafeLog(message string) {
    mu.Lock()
    defer mu.Unlock()
    logger.Println(fmt.Sprintf("{\"level\": \"error\", \"msg\": %q, \"ts\": %q}", 
        message, time.Now().Format(time.RFC3339)))
}
该函数通过sync.Mutex实现写入互斥,防止日志内容交错。每次输出包含时间戳、级别和消息,便于后续分析。
常见问题与最佳实践
  • 避免在日志中记录敏感信息,如密码或令牌
  • 使用异步日志库(如Zap)提升性能
  • 确保日志文件具备适当的滚动策略

4.4 典型应用场景代码示例

数据同步机制
在分布式系统中,配置中心常用于实现服务间的数据同步。通过监听配置变更事件,各节点可实时更新本地缓存。
watcher, err := client.NewWatcher("/config/service_a")
if err != nil {
    log.Fatal(err)
}
for {
    event := watcher.Next()
    if event.Type == config EventTypeUpdate {
        reloadConfig(event.Value) // 重新加载新配置
    }
}
上述代码创建一个配置监听器,当路径/config/service_a下配置发生更新时,触发reloadConfig函数,实现热更新。
多环境配置管理
使用标签区分开发、测试、生产环境配置,通过统一接口按环境拉取:
  • dev: 数据库连接数限制为10
  • prod: 连接池最大扩展至100
  • test: 启用模拟数据注入

第五章:总结与进阶学习建议

持续构建项目以巩固技能
实际项目是检验技术掌握程度的最佳方式。建议每学习一个新框架或工具后,立即构建一个最小可行项目(MVP)。例如,学习 Go 语言后可尝试实现一个轻量级 REST API 服务:

package main

import (
    "encoding/json"
    "net/http"
)

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

func userHandler(w http.ResponseWriter, r *http.Request) {
    user := User{ID: 1, Name: "Alice"}
    json.NewEncoder(w).Encode(user)
}

func main() {
    http.HandleFunc("/user", userHandler)
    http.ListenAndServe(":8080", nil)
}
参与开源社区提升实战能力
贡献开源项目能显著提升代码质量和协作能力。推荐从 GitHub 上的“good first issue”标签入手,逐步熟悉 Pull Request 流程。常见参与路径包括:
  • 修复文档错别字或补充示例
  • 编写单元测试覆盖边缘情况
  • 优化性能瓶颈并提交性能对比数据
系统性学习路径规划
为避免知识碎片化,建议按以下结构规划学习路线:
阶段核心目标推荐资源
初级掌握语言基础与常用库官方文档、A Tour of Go
中级理解并发模型与设计模式《Go in Action》、Go Weekly Newsletter
高级性能调优与系统架构设计Go profiling tools、大型项目源码分析
现代C++并发编程实战:从数据竞争到线程池实现 并发编程是现代软件开发的核心技术之一,它允许多个任务在重叠的时间段内执行,从而提升程序在多核处理器上的执行效率。其基本原理是通过创建多个执行流(线程)来同时处理任务,关键技术包括线程管理、数据同步和异步任务处理。并发编程的核心技术价值在于充分利用硬件资源,解决程序性能瓶颈,提升系统吞吐量和响应速度。典型应用场景包括高性能计算、服务器后端处理、图形渲染和实时数据处理等领域。本文聚焦于现代C++(C++11及以上)的并发编程实战,深入探讨了**数据竞争**的成因与危害,并通过**互斥锁**、**原子操作**等机 阅读详情

相关推荐

C++经典项目源码深度解析:从JSON库到高并发服务器实战

C++作为一门高性能系统编程语言,其核心价值在于构建复杂、高效的软件系统。理解其底层原理,如内存管理、并发模型和数据结构,是掌握工程实践的关键。通过剖析经典开源项目的源码,开发者能直观学习如何将这些基础概念应用于实际场景,例如利用RAII和智能指针实现安全的资源管理,或运用I/O多路复用和线程池设计高并发服务。这种从概念到实现的映射,能极大提升解决实际问题的能力。本文以JSON解析库和网络服务器为例,深入探讨了Pimpl惯用法、事件驱动架构等核心模式,展示了如何通过阅读源码,将零散的语法知识整合为系统的工程

weixin_34284188的博客 481

【一】 C++编程入门知识总结 | 内附完整的源代码示例(有版权问题,私聊删除)

C++入门基础知识《黑马教程》的总结归纳

weixin_45615071的博客 1万+

C/C++新手入门项目合集:GitHub资源库0voice/introduce_c-cpp_manual详解

C/C++作为系统级编程语言,其核心价值在于对计算机底层原理的深入理解和高效控制。从内存管理、指针操作到数据结构与算法,掌握这些基础概念是构建高性能应用的关键。在工程实践中,理论学习之后往往面临“如何将知识转化为实际项目”的痛点。开源社区为此提供了丰富的练手资源,其中GitHub上的项目合集成为开发者从入门到进阶的重要桥梁。这些资源覆盖了网络编程、并发处理、图形界面等核心应用场景,通过阅读和修改真实代码,能够有效提升工程思维和调试能力。本文聚焦的 `0voice/introduce_c-cpp_manual

weixin_30546189的博客 338

C++养成教程(从入门到精通)

前言 最近因工作需要学习C++,决定开创一个养成系的博客,和大家共同进步。之后我会把学习过程中的所有代码写入这篇博客里,由于中途出现的这一想法,所以更新内容会从中间部分开始,之后会陆续补充完善。 ...

seventonight的博客 1815

C++开源项目阅读指南:从网络编程到系统优化的学习路径

开源项目是学习现代C++编程范式和工程实践的重要途径。理解其核心原理,如事件驱动、内存管理和数据结构设计,能帮助开发者掌握高性能、高并发系统的构建方法。这些技术广泛应用于网络服务器、基础设施库和存储引擎等场景,对提升代码质量和系统性能具有重要价值。本文聚焦于网络编程、工具库和系统优化等领域的精选项目,如TinyWebServer和LevelDB,通过分析其架构设计和实现细节,为读者提供一套高效的学习方法论。

weixin_30536861的博客 319

C#/.NET 从入门到精通:系统学习路径与实战指南

面向对象编程是现代软件开发的核心范式,它通过封装、继承和多态等机制组织代码,提升可维护性和复用性。其原理在于将数据与操作数据的方法绑定为对象,并通过类和接口定义契约。这一范式在构建复杂企业应用时价值显著,能有效管理状态与行为。在C#/.NET技术栈中,异步编程和LINQ是两大关键技术,它们分别用于高效处理I/O密集型任务和声明式数据查询,广泛应用于Web服务、数据处理等场景。本文聚焦于如何系统掌握C#/.NET,特别是通过深入理解异步编程和LINQ等核心概念,构建从语法基础到微服务架构的完整知识体系,为开发

chuange6363的博客 662

终极指南:如何用C/C++高效运行大语言模型llama.cpp

想要在本地高效运行大语言模型,又不想依赖复杂的Python环境?llama.cpp正是你需要的解决方案!这个用纯C/C++编写的大语言模型推理框架,让你能在各种硬件上实现高性能的LLM推理,从个人电脑到移动设备,再到服务器集群。无论你是开发者、研究人员还是技术爱好者,掌握llama.cpp都能让你在AI应用开发中占据先机。 ## 🚀 项目哲学:极简主义的AI推理引擎 llama.cpp的核心

gitblog_01154的博客 452

C++集成AI实战:三步实现提示词驱动开发,告别复杂模型部署

在软件工程领域,API集成与微服务架构已成为现代应用开发的核心模式,它允许开发者将复杂功能封装为可远程调用的服务。其基本原理是通过定义清晰的接口协议(如HTTP/JSON),实现不同系统组件间的解耦与协同。这种模式的技术价值在于极大地提升了开发效率、降低了系统复杂度,并实现了能力的快速复用与迭代。在人工智能应用场景中,传统的集成方式往往需要本地部署庞大的深度学习模型,引入沉重的依赖和运维负担。而提示词驱动开发(Prompt-driven Development)正是这一架构思想的延伸,它将AI模型的核心“推

484

C/C++开源项目学习路线:从入门到精通实战指南

学习开源项目是提升C/C++编程能力的核心路径,其关键在于理解从代码到可运行系统的完整工程链路。其原理在于通过阅读、调试和模仿真实项目代码,掌握语法巩固、内存管理、系统调用等基础概念,进而理解模块化设计、设计模式等高级架构思想。这一过程的技术价值在于能将书本知识转化为解决实际问题的工程能力,例如构建网络服务器、图形应用或嵌入式系统。应用场景广泛,包括夯实编程基础、准备技术面试、完成课程设计,以及深入特定技术领域进行研发。本文聚焦于C/C++开源项目,提供了从《TinyWebServer》等入门项目到《Chr

weixin_33843409的博客 456

C++怎么学?零基础也可以,一份完整学习路线图助你搞定!

提供一条清晰、系统且实践导向的C++学习路径。从基础语法、面向对象、泛型编程、异常处理,逐步深入STL、智能指针、并发编程等现代C++高级特性。无论是新手还是希望提升,通过系统学习与项目实践,助您将理论知识转化为实际解决问题的能力。

Lion_Long的博客 4029

VC++从入门到精通1—5章源代码

基础知识篇

车载 Android C++ 完整技能路线:从基础到进阶

本文系统梳理了车载Android C++开发的完整技能路线,从基础到进阶分为五个阶段:C++语言基础、Android原生基础、Framework进阶、车载专项技能和高级优化。重点涵盖现代C++特性、Boost库应用、Binder IPC通信、HAL层开发、SOA架构、SOME/IP协议等核心技术,特别强调车载领域特有的无锁队列、内存治理和稳定性要求。针对车载开发者的现状,指出需要补齐Binder Native链路、AOSP源码调试和SOA架构思想三大短板。该路线完整覆盖了车载Android底层开发的核心技术

wg_mlo的博客 434

c++从入门到精通(第2版)

c++从入门到精通第二版,中文高清完整版。

[特殊字符] 专栏标题:《C++ 极客实战:从现代语言特性到工业级架构》

C++ 的学习曲线虽然陡峭,但一旦跨越了那道门槛,你将获得对计算机系统无与伦比的掌控力。这不是一个教你“怎么写 C++”的专栏,而是一个教你“如何像专家一样思考 C++”的旅程。如果你准备好迎接挑战,提升自己的技术壁垒,请点击订阅。我在代码的世界里,等你一起探讨。👉 立即订阅,开启你的 C++ 进阶之路!

huahua8088的博客 1161

C++从入门到精通 源代码

C++从入门到精通,王牌资源 C++源码,可供参考!对编程很有用

Visual C++从入门到精通第三版.pdf

比较好的入门书.共享给大家.从最基础的C++讲起到VC高级应用. 比较好的入门书.共享给大家.从最基础的C++讲起到VC高级应用.

Brynet终极指南:快速掌握高性能C++网络库的完整教程

Brynet是一款专为C++开发者设计的高性能TCP网络库,采用Header-Only架构,仅需C++11编译器即可运行,无任何第三方依赖。这个轻量级网络库支持跨平台开发,为构建稳定高效的网络服务提供了强大支持。无论你是网络编程新手还是经验丰富的开发者,Brynet都能帮助你快速搭建高性能的服务器应用。 ## 🚀 项目亮点速览:为什么选择Brynet? Brynet作为现代C++网络库的杰出

gitblog_01010的博客 580

IEEE GRSL投稿全流程避坑指南:从Latex模板到校样缴费,新手必看的7个关键节点

本文详细解析了IEEE GRSL投稿全流程中的7个关键节点,从LaTeX模板选择到校样缴费,帮助新手避免常见陷阱。特别针对图像格式转换、审稿意见回复、版权转移等环节提供实用技巧,确保投稿过程高效顺利。

weixin_28720573的博客 573

从Qt入门到精通:掌握框架思维、并发编程与性能优化实战

在桌面与嵌入式应用开发领域,Qt作为一个成熟的跨平台C++框架,其核心价值远不止于界面构建。理解其元对象系统、信号槽机制和模型/视图架构等底层原理,是构建可扩展、高性能应用的基础。这些机制不仅实现了灵活的对象通信与内存管理,还为复杂的数据展示与交互提供了高效解耦方案。在实际工程中,面对高并发场景,开发者需要深入掌握Qt的线程模型、线程安全的数据共享策略以及性能优化技巧,以确保应用的响应速度与稳定性。本文聚焦于如何跨越从基础使用到解决复杂工程问题的鸿沟,通过剖析**元对象系统**与**多线程编程**等关键技术

weixin_34122604的博客 529

uniapp原生安卓插件开发之路

声明: 我的功能需求:全局悬浮框,也就是在其他应用上依旧能悬浮。查遍uni文档,无法实现,甚至native.js也无法实现,无奈只能走上原生插件开发这条不归路了。(吐槽:天天写前端js,突然写了下android简直要炸,public static String啥的,var不香嘛,不过别人严谨肯定还是有别人道理的,加油吧,争取能入个门!) 本人不懂安卓开发,完全安卓小白,所以安卓方面可能表达的不是很好,安卓代码可能写的不优美,但我功能最终还是实现了。但是为了需求,能力总能被“逼”出来。有描述错误的还望指正

Jone's Area 1万+

如何用AP525 测试输入信号的相位,频响,延时,Pop和卡顿

假设信号不是从AP输出,是功放或者其他的播放设备输出的信号,接入到AP525的输入端。

帮助100万人颠覆负面潜意识,实现人生蜕变,培养1万名潜意识开发导师 2406
上一篇: C++与Python混合编程避坑指南(90%新手都会犯的3大错误)
下一篇: std::promise使用避坑指南,资深架构师不愿透露的4个实战经验
quickcode
博客等级 码龄19年 146粉丝 1996原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值