多线程同步实战指南:Python、Java与Go的等待之道

并发同步概览

在现代软件开发中,并发编程已成为提升系统性能的关键手段。然而,并发带来的不仅仅是性能提升,还有复杂的同步问题。当我们需要协调多个并发任务时,如何优雅地等待它们完成成为了每个开发者必须面对的挑战。

Python、Java 和 Go 作为三种主流的编程语言,分别提供了不同的解决方案:

  1. Python的concurrent.futures.wait函数
  2. Java的CountDownLatch并发同步类
  3. Go的sync.WaitGroup sync包的WaitGroup结构体

并发同步介绍

Java CountDownLatch

CountDownLatch 是 Java 并发包 (java.util.concurrent)中一个强大的同步工具类,它允许一个或多个线程等待其他线程完成操作。

scss 复制代码
// 初始化计数器(需要等待3个任务)
CountDownLatch latch = new CountDownLatch(3);

// 工作线程
for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        try {
            // 模拟工作
            Thread.sleep((long)(Math.random() * 1000));
            System.out.println(Thread.currentThread().getName() + " 完成工作");
            latch.countDown(); // 任务完成,计数器减1
        } catch (InterruptedException e) {
           
        }
    }).start();
}

// 主线程等待
try {
    latch.await(); // 阻塞直到计数器归零
    System.out.println("所有任务已完成,继续主线程工作");
} catch (InterruptedException e) {
}

CountDownLatch 基于计数器实现。初始化时设定计数值(需要等待的事件/任务数量),工作线程完成任务后调用 countDown()递减计数器,主线程调用 await()阻塞直到计数器归零,计数器归零后不能重复使用。

Python的concurrent.futures.wait函数

concurrent.futures.wait是 Python 标准库中用于并发任务同步的重要工具,它提供了灵活的等待机制来管理多个 Future 对象的完成状态。

python 复制代码
截取实际项目中的一段代码
task_future_list = []
with ThreadPoolExecutor(len(image_list)) as executor:
    for detect_image_item in image_list:
        task_future = executor.submit(self.__extract_search, db_name, detect_image_item)
        task_future_list.append(task_future)
    主线程阻塞在此处等待所有task_future的完成
    wait(task_future_list, return_when='ALL_COMPLETED')
search_list = []
for task in task_future_list:
    try:
        search_item = task.result()
        search_list.appendsearch_item)
    except Exception as e:
        log.exception(e)
return search_list

配合线程池使用,将executor.submit返回的future放入task_future_list中,当return_when='ALL_COMPLETED',主线程阻塞在wait函数处等待所有future的完成。return_when还有 等待任意一个任务完成(FIRST_COMPLETED)或等待首个异常出现(FIRST_EXCEPTION)可供选择。

Go的sync.WaitGroup结构体

sync.WaitGroup是sync包中提供的轻量级同步原语,基于原子操作的轻量级实现,可以动态计数管理、可重用性、与goroutine完美配合

go 复制代码
// sync.WaitGroup是一个结构体,它的零值是一个可以直接使用的有效 WaitGroup,其内部计数器初始化为 0。
//go语言的显著特性,声明结构体后里面的属性赋予零值
var wg sync.WaitGroup

// 计数可以动态调整,不需要预先知道确切数量,注意不要放进goroutine里面去执行wg.Add()方法
for i := 0; i < 5; i++ {
    wg.Add(1) // 可以多次调用Add
    go func(id int) {
        defer wg.Done()
        // 工作逻辑
    }(i)
}
wg.Wait() 

sync.WaitGroup通过精心设计的结构体内核,实现了极其简单的API(Add/Done/Wait)并且基于原子操作和信号量实现了无锁编发,它完美体现了Go语言"少即是多"的设计哲学。

并发同步总结

每种工具都在性能、易用性和功能强大性之间找到了不同的平衡点。选择哪种工具不仅取决于技术需求,更应该考虑团队的技术栈熟悉度和项目的长期维护需求。 最终,优秀的并发编程不在于使用最复杂的技术,而在于选择最适合当前场景的解决方案。这三种同步工具各有所长,理解它们的特性将帮助我们在实际项目中做出更明智的技术选型。

相关推荐
东风破_2 小时前
danci 2:创建的单词书到底存在哪里?从 Supabase 一路理解 ORM、Drizzle 和 RLS
数据库·后端·node.js
东风破_3 小时前
danci 项目(三):从 JSON 数据到 AI Coding,真实项目里的数据清洗、Prompt 和工程规范
前端·后端·node.js
九皇叔叔3 小时前
《MySQL 体系架构详解:Server 层、SQL 执行流程与 InnoDB、MyISAM、MEMORY 存储引擎》
sql·mysql·架构
怕浪猫3 小时前
FDE 最大的浪费不是写出有 bug 的代码,而是漂亮地解决了一个错误的问题
面试·架构·github
东风破_3 小时前
danci 项目(一):从需求到架构,一个单词学习系统为什么会这样设计
前端·后端·node.js
jay神4 小时前
【计算机毕业设计】基于SpringBoot的程序教学辅助系统
java·前端·vue.js·spring boot·后端·毕业设计·课程设计
ZGIAI5 小时前
ZGI 迭代节点:批量资料的逐项处理
人工智能·架构
ZGIAI5 小时前
ZGI 知识检索:让业务回答有据可查
人工智能·架构
天远Date Lab6 小时前
零信任架构实战:基于天远双人婚姻评估查询构建自动化房产按揭联合审查网关
运维·人工智能·架构·自动化
黄敬峰6 小时前
Next.js 全栈实战:数据清洗、ORM 设计与 AI Prompt 工程最佳实践
面试·github