多线程同步实战指南: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语言"少即是多"的设计哲学。

并发同步总结

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

相关推荐
怕什么真理无穷19 分钟前
C++_面试15_零拷贝
c++·面试·职场和发展
在人间负债^42 分钟前
Rust 实战项目:TODO 管理器
开发语言·后端·rust
Moonbit44 分钟前
入围名单公布|2025 MGPIC 决赛即将拉开帷幕!
后端·算法
沐怡旸1 小时前
【穿越Effective C++】条款21:必须返回对象时,别妄想返回其reference——对象返回的语义与效率平衡
c++·面试
爱吃烤鸡翅的酸菜鱼1 小时前
用【rust】实现命令行音乐播放器
开发语言·后端·rust
黛琳ghz1 小时前
用 Rust 从零构建高性能文件加密工具
开发语言·后端·rust
悟世君子1 小时前
Rust 开发环境搭建
开发语言·后端·rust
OlahOlah1 小时前
Go 入门实战:音乐专辑管理 API
后端
黛琳ghz1 小时前
用 Rust 打造高性能 PNG 压缩服务
开发语言·后端·rust
回家路上绕了弯1 小时前
订单超时自动取消:从业务场景到技术落地的完整设计方案
分布式·后端