Java与Go的并发世界:理解Work Sharing与Work Stealing

news2024/11/24 14:46:05

概述

最近在理解Golang中的Per P概念,于是我就去Go的源码中挖呀挖,结果挖到了Go的调度器设计。

Scalable Go Scheduler Design Doc

Golang的调度器设计文档提到了Go中的P(OS线程)调度器使用的是work-stealing调度算法论文。

Scheduling Multithreaded Computationsby Work Stealing

论文中提到了两个多线程调度算法:work sharing和work stealing。

scheduling multithreaded computations

什么是work sharing?
什么是work stealing?

Work Sharing(工作共享)

先来个专业解释:

在工作共享策略中,当一个处理器生成新的线程时,调度器会尝试将部分线程迁移到其他处理器上,以期分派工作到未充分利用的处理器。这样,工作可以在处理器之间共享,从而实现并行计算。

有点抽象,我个人理解是这样:work sharing:工作共享,是多核多线程并行处理任务的调度算法。 它的基本思想是把任务均匀的分摊到任务执行节点(这里的计算节点就是OS线程)。

Work sharing算法每个任务执行节点使用共享的任务队列,也叫全局队列,调度器负责分配任务给这些任务执行节点。

应用层

我们身处应用层的小开发怎么来理解。我觉得对于Java程序员来说,work sharing就是线程池(ThreadPoolExecturor)。Java的ThreadPoolExecturor有一个共享的任务队列,有一个或者多个任务线程。所有的任务线程都从共享的任务队列中获取任务并执行。

Java ThreadPoolExecutor diagram

优劣

  1. 优点
    因为所有的任务都由调度器统一分配,所以可以保证所有的处理器都保持负载均衡,避免了某些处理器空闲而其他处理器繁忙的情况。

  2. 缺点
    Work Sharing 要求处理器间经常进行任务交换,这可能会带来一些额外的开销。例如上下文切换的成本和处理器间的通信成本等。同时,这样的设计也使得调度器成为了一个明显的瓶颈。

Work Stealing(工作窃取)

在工作窃取调度器中,计算机系统中的每个处理器都有一个需要执行的工作项(计算任务,线程)队列。每个工作项由一系列顺序执行的指令组成,但是在执行过程中,工作项也可能生成新的工作项,这些新的工作项可与其它工作并行执行。这些新产生的工作项最初会被放在执行当前工作项的处理器队列中。当一个处理器的工作执行完毕,它会查看其他处理器的队列并“窃取”他们的工作项。实际上,工作窃取将调度工作分摊到空闲的处理器上,并且只要所有处理器都有工作要做,就不会产生调度开销。

我们身处应用层的小开发怎么来理解。其实就是每个线程有一个自己的任务队列(这样任务队列就去中心化了),每个线程先从自己的线程本地来获取任务执行,但是当自己的任务队列空了就从其他线程的任务队列中“偷”一些任务继续执行。

work stealing diagram

work sharing vs work stealing

我们来对比一下work sharing和work stealing:

Work SharingWork Stealing
线程分配方式当一个处理器产生新线程时,调度器将线程迁移至其他处理器闲置处理器主动从忙碌处理器窃取线程执行
适用情况所有处理器均有工作任务时,能保持负载均衡一部分处理器空闲,一部分处理器忙碌时,能主动窃取任务,保持负载均衡
线程迁移频率总是会发生线程迁移,可能会影响性能在所有处理器都有任务执行的情况下不迁移线程,减少了线程调度开销
主动/被动被动分配 – 调度器主动将任务分配给处理器主动窃取 – 处理器主动从其他处理器窃取任务
实现复杂性实现起来相对简单,易于理解实现起来相对复杂,需要处理窃取行为等细节问题
典型应用实例Java中的ThreadPoolExecutorJava中的ForkJoinPool

ForkJoinPool解析

在Java中,ForkJoinPool实现了Work stealing,我们来看下ForkJoinPool中work stealing相关的组件:

  1. 工作线程
  2. 任务队列
  3. 调度器
  4. 任务
  5. 工作窃取策略
  6. 同步机制

工作线程

ForkJoinPool中Work Thread类为:ForkJoinWorkerThread,我们来看下构造函数:


// 根据指定的线程组和ForkJoinPool创建新的ForkJoinWorkerThread
// 确定是否使用系统类加载器和是否清除线程本地变量
super(group, null, pool.nextWorkerThreadName(), 0L, !clearThreadLocals);

// 保存ForkJoinPool,并且获取并保存uncaughtExceptionHandler
UncaughtExceptionHandler handler = (this.pool = pool).ueh;

// 为当前工作线程创建新的ForkJoinPool.WorkQueue
this.workQueue = new ForkJoinPool.WorkQueue(this, 0);

// 如果需要清除线程本地变量,则设置清除标志
if (clearThreadLocals)
    workQueue.setClearThreadLocals();

// 设置线程为守护线程
super.setDaemon(true);

// 如果有uncaughtExceptionHandler,则设置给当前线程
if (handler != null)
    super.setUncaughtExceptionHandler(handler);

// 如果需要使用系统类加载器,则设置系统类加载器为当前线程的上下文类加载器
if (useSystemClassLoader)
    super.setContextClassLoader(ClassLoader.getSystemClassLoader());

COPY

以上代码中,我们看到工作线程有一个workQueue。

任务队列

任务队列ForkJoinPool是双端队列:从队列头部获取任务,从队列尾部窃取任务。

我们只关注构造函数:

工作队列关联了一个ForkJoin工作线程,并且任务队列的内存存储使用了环形缓冲区(Ring Buffer)来存储任务。


    /**
     * 构造函数。对于拥有队列,大多数字段是在pool.registerWorker中的线程启动时初始化的。
     *
     * @param owner 所有者,此工作队列属于哪个 ForkJoinWorkerThread
     * @param config 配置参数,可能包括了如何处理该工作队列的各种配置
     */
    WorkQueue(ForkJoinWorkerThread owner, int config) {
        // 设置此工作队列的所有者为指定的 ForkJoinWorkerThread
        this.owner = owner;

        // 设置此工作队列的配置为指定的配置
        this.config = config;

        // 初始化 base 和 top 表示队列的头和尾部索引为 1
        base = top = 1;
    }

COPY

调度器

ForkJoinPool的调度器主要是负责任务提交,任务队列管理,工作线程和工作窃取的处理。

在了解ForkJoinPool任务提交之前我们得先了解一下任务队列, 在ForkJoinPool中任务队列是一个数组存放了所有工作线程的任务队列,在提交任务时会随机从这个任务队列数组中选取一个然后把任务提交到这个任务队列中:


WorkQueue[] queues;                  // main registry

COPY

如下图:

file

提交任务

提交任务函数:


 private <T> ForkJoinTask<T> poolSubmit(boolean signalIfEmpty,
                                           ForkJoinTask<T> task) 

COPY

提交任务流程:

提交任务

任务窃取

任务窃取是ForkJoinPool的核心了吧,窃取主要涉及两个核心方法:scanawaitWork


while ((src = scan(w, src, r)) >= 0 || (src = awaitWork(w)) == 0);

COPY

scan

scan流程:

  1. 首先从自己的任务队列中获取一个任务并执行
  2. 如果自己的任务队列中就尝试从其他任务队列队尾窃取任务

这个函数代码有点麻烦,有点费脑子,大家自己看吧。

总结

Work stealing可以提升并发和并行,因为空闲的线程会主动窃取其他线程中任务队列的任务从而提升并行处理能力。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1561880.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ETL工具-nifi干货系列 第六讲 处理器JoltTransformJSON

1、处理器作用 使用Jolt转换JSON数据为其他结构的JSON,成功的路由到success,失败的failure。处理JSON的实用程序不是基于流的&#xff0c;因此大型JSON文档转换可能会消耗大量内存。 Jolt&#xff1a;JSON 到 JSON 转换库&#xff0c;用 Java 编写&#xff0c;其中转换的 &qu…

自动驾驶---Motion Planning之轨迹Speed优化

1 背景 在之前的几篇文章中&#xff0c;不管是通过构建SL图《自动驾驶---Motion Planning之Path Boundary》&#xff0c;ST图《自动驾驶---Motion Planning之Speed Boundary》&#xff0c;又或者是构建SLT图《自动驾驶---Motion Planning之构建SLT Driving Corridor》&#xff…

vivado 配置存储器器件编程2

为双 QSPI (x8) 器件创建配置存储器文件 您可使用 write_cfgmem Tcl 命令来为双 QSPI (x8) 器件生成 .mcs 镜像。此命令会将配置数据自动拆分为 2 个独立 的 .mcs 文件。 注释 &#xff1a; 为 SPIx8 生成 .mcs 时指定的大小即为这 2 个四通道闪存器件的总大小。…

生产制造园区数字孪生3D大屏展示提升运营效益

在智慧园区的建设中&#xff0c;3D可视化管理平台成为必不可少的工具&#xff0c;数字孪生公司深圳华锐视点打造的智慧园区3D可视化综合管理平台&#xff0c;致力于将园区的人口、经济、应急服务等各项业务进行3D数字化、网络化处理&#xff0c;从而实现决策支持的优化和管理的…

前端二维码生成工具小程序:构建营销神器的技术解析

摘要&#xff1a; 随着数字化营销的不断深入&#xff0c;二维码作为一种快速、便捷的信息传递方式&#xff0c;已经广泛应用于各个领域。本文旨在探讨如何通过前端技术构建一个功能丰富、操作简便的二维码生成工具小程序&#xff0c;为企业和个人提供高效的营销支持。 一、引言…

【目标检测】YOLOv6 的网络结构,图解RepBlock重参数化

YOLOv6 是美团推出的&#xff0c;在这个版本里面&#xff0c;不再使用之前 YOLOv4 和 YOLOv5 的带 CSP 结构的 CSPDarknet-53 作为 backbone 了&#xff0c;而是在 RepVGG 的启发下&#xff0c;推出了新的 EfficientRep 作为 YOLOv6 的 backbone。 RepVGG 最重要的一点是&…

学透Spring Boot 003 —— Spring 和 Spring Boot 常用注解(附面试题和思维导图)

这是 学透 Spring Boot 专栏 的第三篇&#xff0c;欢迎关注我&#xff0c;与我一起学习和探讨 Spring Boot 相关知识&#xff0c;学透 Spring Boot。 从面试题说起 今天我们通过一道和Spring Boot有关的常见面试题入手。 面试题&#xff1a;说说 Spring Boot 中有哪些常用注解…

助力瓷砖生产智造,基于YOLOv5全系列参数【n/s/m/l/x】模型开发构建瓷砖生产制造场景下1280尺寸瓷砖表面瑕疵检测识别系统

砖生产环节一般经过原材料混合研磨、脱水、压胚、喷墨印花、淋釉、烧制、抛光&#xff0c;最后进行质量检测和包装。得益于产业自动化的发展&#xff0c;目前生产环节已基本实现无人化。而质量检测环节仍大量依赖人工完成。一般来说&#xff0c;一条产线需要配数名质检工&#…

Windows系统搭建TortoiseSVN客户端并实现无公网IP访问内网服务端

文章目录 前言1. TortoiseSVN 客户端下载安装2. 创建检出文件夹3. 创建与提交文件4. 公网访问测试 前言 TortoiseSVN是一个开源的版本控制系统&#xff0c;它与Apache Subversion&#xff08;SVN&#xff09;集成在一起&#xff0c;提供了一个用户友好的界面&#xff0c;方便用…

Mysql的基本命令

1 服务相关命令 命令描述systemctl status mysql查看MySQL服务的状态systemctl stop mysql停止MySQL服务systemctl start mysql启动MySQL服务systemctl restart mysql重启MySQL服务ps -ef | grep mysql查看mysql的进程mysql -uroot -hlocalhost -p123456登录MySQLhelp显示MySQ…

使用 Django 构建简单 Web 应用

当我们在使用Django构建Web应用时&#xff0c;通常将会涉及到多个步骤&#xff0c;从创建项目到编写视图、模板、模型&#xff0c;再到配置URL路由和静态文件&#xff0c;最后部署到服务器上。所以说如果有一个环节出了问题&#xff0c;都是非常棘手的&#xff0c;下面就是我们…

vim copilot插件安装使用

copilot简介 在使用不熟悉的开发语言或函数库进行开发工作时&#xff0c;虽然可以通过阅读开发文档或示例代码的方式学习开发&#xff0c;但这种方式学习成本较高、效率较低&#xff0c;且后续不一定会用上。 GitHub Copilot是一个由GitHub开发的机器学习工具&#xff0c;可以…

加密软件VMProtect教程:使用脚本-功能

VMProtect是新一代软件保护实用程序。VMProtect支持德尔菲、Borland C Builder、Visual C/C、Visual Basic&#xff08;本机&#xff09;、Virtual Pascal和XCode编译器。 同时&#xff0c;VMProtect有一个内置的反汇编程序&#xff0c;可以与Windows和Mac OS X可执行文件一起…

HTTP的介绍

一.什么是HTTP&#xff1f; Hyper Text Transfer Protocol,超文本传输协议&#xff0c;规定了浏览器和服务器之间数据传输的规则。 二.HTTP的特点 &#xff08;1&#xff09;基于TCP协议&#xff1a;面向连接&#xff0c;安全 &#xff08;2&#xff09;基于请求-响应模型的&…

壁纸小程序Vue3(自定义头部组件)

1.自定义头部 coustom-nav <view class"layout"><view class"navbar"><view class"statusBar"></view><view class"titleBar"><view class"title">标题</view><view class&qu…

网络安全 | 什么是网络安全?

关注WX&#xff1a;CodingTechWork 网络安全 网络安全-介绍 网络安全是指用于防止网络攻击或减轻其影响的任何技术、措施或做法。网络安全旨在保护个人和组织的系统、应用程序、计算设备、敏感数据和金融资产&#xff0c;使其免受简单而不堪其绕的计算机病毒、复杂而代价高昂…

RabbitMQ安装及Springboot 集成RabbitMQ实现消息过期发送到死信队列

死信队列 RabbitMQ 的死信队列&#xff08;Dead-Letter-Exchanges&#xff0c;简称 DLX&#xff09;是一个强大的特性&#xff0c;它允许在消息在队列中无法被正常消费&#xff08;例如&#xff0c;消息被拒绝并且没有设置重新入队&#xff0c;或者消息过期&#xff09;时&…

在 Three.js 中,`USDZExporter` 类用于将场景导出为 USDZ 格式,这是一种用于在 iOS 平台上显示增强现实(AR)内容的格式。

demo 案例 在 Three.js 中&#xff0c;USDZExporter 类用于将场景导出为 USDZ 格式&#xff0c;这是一种用于在 iOS 平台上显示增强现实&#xff08;AR&#xff09;内容的格式。下面是关于 USDZExporter 的入参、出参、方法和属性的讲解&#xff1a; 入参 (Parameters): sc…

解决Quartus与modelsim联合仿真问题:# Error loading design解决,是tb文件中没加:`timescale 1ns/1ns

解决Quartus与modelsim联合仿真问题&#xff1a;# Error loading design解决&#xff0c;是tb文件中没加&#xff1a;timescale 1&#xff0c;一直走下来&#xff0c;在modelsim中出现了下面问题2&#xff0c;rtl文件、tb文件2.1&#xff0c;rtl代码2.2&#xff0c;tb测试2.3&a…

C# WPF编程-Application类(生命周期、程序集资源、本地化)

C# WPF编程-Application类 应用程序的生命周期创建Application对象应用程序的关闭方式应用程序事件 Application类的任务显示初始界面处理命令行参数访问当前Application对象在窗口之间进行交互 程序集资源添加资源检索资源pack URI内容文件 本地化构建能够本地化的用户界面 每…