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

并发同步总结

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

相关推荐
kyriewen10 小时前
别再 console.log 了:5 个 Chrome DevTools 调试技巧,用过就回不去了
前端·javascript·面试
Csvn12 小时前
SSH 远程管理与安全加固 — 运维的守门之道
后端
IT_陈寒12 小时前
Python搞不定字符串编码?这破玩意坑我两小时!
前端·人工智能·后端
菜鸟谢13 小时前
Rust 智能指针完整详解
后端
GuWenyue14 小时前
排序效率低?5分钟吃透快速排序,性能飙升至O(nlogn)
前端·javascript·面试
菜鸟谢14 小时前
Rust 函数完整知识点详解
后端
爱勇宝14 小时前
淡泊名利之前,先承认我们都很焦虑
前端·后端·程序员
菜鸟谢14 小时前
Rust 闭包(Closure)完整详解
后端
ServBay14 小时前
如何利用本地技术栈构建 0 成本 AI SaaS 雏形
后端·aigc·ai编程
菜鸟谢14 小时前
Rust 集合 + 迭代器完整详解
后端