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

并发同步总结

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

相关推荐
摇滚侠4 小时前
Spring Boot3零基础教程,RedisTemplate 定制化,笔记70
spring boot·笔记·后端
刘一说4 小时前
深入浅出 Spring Boot 自动配置(Auto-Configuration):原理、机制与最佳实践
java·spring boot·后端
程序员小假4 小时前
我们来说一说什么是联合索引最左匹配原则?
java·后端
豆苗学前端4 小时前
企业级用户登录Token存储最佳实践,吊打面试官
前端·javascript·后端
LSTM974 小时前
使用 C# 打印 PDF 文档:基于 Spire.PDF 的实战教程
后端
我命由我123454 小时前
PDFBox - PDF 页面坐标系、PDF 页面尺寸获取、PDF 页面位置计算
java·服务器·开发语言·笔记·后端·java-ee·pdf
文心快码BaiduComate4 小时前
冰城码力全开,共赴AI Coding英雄之旅!CEDxCNCC百度文心快码Meetup圆满落幕!
前端·后端·程序员
sp424 小时前
试探构建一个简洁、清晰的 Java 日期 API
java·后端