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

并发同步总结

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

相关推荐
Victor35611 小时前
Netty(20)如何实现基于Netty的WebSocket服务器?
后端
缘不易11 小时前
Springboot 整合JustAuth实现gitee授权登录
spring boot·后端·gitee
Kiri霧11 小时前
Range循环和切片
前端·后端·学习·golang
WizLC12 小时前
【Java】各种IO流知识详解
java·开发语言·后端·spring·intellij idea
Victor35612 小时前
Netty(19)Netty的性能优化手段有哪些?
后端
爬山算法12 小时前
Netty(15)Netty的线程模型是什么?它有哪些线程池类型?
java·后端
Solar202512 小时前
TOB企业智能获客新范式:基于数据驱动与AI的销售线索挖掘与孵化架构实践
人工智能·架构
白宇横流学长12 小时前
基于SpringBoot实现的冬奥会科普平台设计与实现【源码+文档】
java·spring boot·后端
yaoh.wang12 小时前
力扣(LeetCode) 1: 两数之和 - 解法思路
python·程序人生·算法·leetcode·面试·跳槽·哈希算法
Python编程学习圈13 小时前
Asciinema - 终端日志记录神器,开发者的福音
后端