不同开发语言在进程、线程和协程的设计差异

不同开发语言在进程、线程和协程的设计差异

  • 1. 进程、线程和协程上的差异
    • 1.1 进程、线程、协程的定义
    • 1.2 进程、线程、协程的差异
    • 1.3 进程、线程、协程的内存成本
    • 1.4 进程、线程、协程的切换成本
  • 2. 线程、协程之间的通信和协作方式
    • 2.1 python如何实现线程通信?
    • 2.2 java如何实现线程通信?
    • 2.3 go如何实现线程通信?
  • 3. 常用线程池的实现和使用方式
    • 3.1 python常用线程池
    • 3.2 java常用线程池
    • 3.3 go常用线程池
  • 4. 疑问和思考
    • 4.1 go语言中,协程的成本已经很低,还有必要使用线程池吗?
  • 5. 参考文档

在多线程项目开发时,最常用、最常遇到的问题是
1,线程、协程安全
2,线程、协程间的通信和控制

本文主要探讨不同开发语言go、java、python在进程、线程和协程上的设计和开发方式的异同。


1. 进程、线程和协程上的差异

1.1 进程、线程、协程的定义

  • 进程
    进程是操作系统进行资源分配的基本单位,每个进程都有自己的独立内存空间,不同的进程之间无法相互干扰。由于进程比较重,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。

  • 线程
    线程又叫做轻量级进程,是进程的一个实体,是处理器任务调度和执行的基本单位位(能够申请到cpu资源执行相关任务)。它是比进程更小的能独立运行的基本单位。线程只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
    线程的执行需要申请对应的cpu资源,因此线程切换涉及CPU的资源切换(保存cpu上下文、触发软中断暂停当前线程、从就绪线程中选择一个执行),过程中会涉及用户态 -> 内核态(切换cpu)-> 用户态的切换,因此开销比较大。

  • 协程
    协程,又称微线程,是一种用户态的轻量级线程,协程的调度完全由用户控制(也就是在用户态执行)。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到线程的堆区,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快(协程切换,线程不变,因此不需要切换cpu,不进行内核态切换,成本较低)。

进程、线程、协程之间的关系可以如下图诠释
在这里插入图片描述

1.2 进程、线程、协程的差异

线程进程的区别

  1. 根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位,cpu运行任务是运行线程
  2. 资源开销:每个进程都有独立的代码和数据空间,程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一进程的线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器,线程之间切换的开销小。
  3. 包含关系:如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的。
  4. 内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的。
  5. 影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
  6. 执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。两者均可并发执行。

协程与线程的区别:

  1. 一个线程可以有多个协程。
  2. 大多数业务场景下,线程进程可以看做是同步机制,而协程则是异步。
  3. 线程是抢占式,而协程是非抢占式的,所以需要用户代码释放使用权来切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力。
  4. 协程并不是取代线程,而且抽象于线程之上。线程是被分割的CPU资源, 协程是组织好的代码流程, 协程需要线程来承载运行。

1.3 进程、线程、协程的内存成本

进程占用内存

  • 32 位操作系统只支持 4G 内存的内存条,这是因为进程在 32 位操作系统中最多只能占用 4G 内存
  • 在 64 位操作系统中可以占用更多内存。

线程占用内存

  • 一般是 10MB,不同的操作系统版本之间有些差异,区间在 4M - 64M。

协程占用内

  • 一个协程占用 2KB 左右的内存

内存占用: 进程 >> 线程 >> 协程

更低的内存占用代表着更低的资源切换成本和可以提供更高的并发。

1.4 进程、线程、协程的切换成本

进程切换,需要执行如下2个步骤

  1. 切换页目录以使用新的地址空间(切换虚拟内存空间)
  2. 切换内核栈和硬件上下文(切换cpu资源)

线程切换

  1. 使用的是进程的内存资源,不需要切换虚拟内存空间
  2. 切CPU换上下文时,需要耗费 CPU 时间,但是进程切换的开销相差不大(几微秒)。

协程切换

  1. 在用户空间发生,不需要切换cpu,只需要切换简单CPU寄存器状态
  2. 一次协程的上下文切换最多需要几十纳秒的时间。

切换成本: 进程切换 > 线程切换 > 协程切换

2. 线程、协程之间的通信和协作方式

线程、协程之间的通信主要用于2个目的

  • 控制线程、协程的执行顺序(触发条件、逻辑启停等)
  • 线程、协程之间传递信息,用于在不同线程、协程之间实现业务逻辑
  • 感知子线程、协程是否已经执行完成

2.1 python如何实现线程通信?

通常使用如下方法进行线程同步,可以根据实际情况调整

  • 共享变量
  • queue,queue本身是线程安全的

更多可以参考 python的多线程及线程间的通信方式

2.2 java如何实现线程通信?

通常使用如下方法进行线程同步,可以根据实际情况调整

  1. 锁与同步
  2. 等待/通知机制
  3. 信号量
  4. 管道

更多可以参考 Java线程间的通信

2.3 go如何实现线程通信?

在go中,常用的是协程(goroutine)进行多并发,因此探讨的通信方式都是以协程(goroutine)进行讨论。

实现多个goroutine间的同步与通信大致有:

  • 全局共享变量
  • channel通信(CSP模型)
  • Context包

这3种方法具体实现可以参考文档 深入golang之—goroutine并发控制与通信

3. 常用线程池的实现和使用方式

3.1 python常用线程池

线程池的基类是 concurrent.futures 模块中的 Executor,Executor 提供了两个子类,即 ThreadPoolExecutor 和 ProcessPoolExecutor,其中 ThreadPoolExecutor 用于创建线程池,而 ProcessPoolExecutor 用于创建进程池。

由于全局GIL锁存在,python多线程本质上同一时间只能1个线程在执行,并不能高效的利用所有的CPU核心。
1, 如果使用多线程,线程的类型基本都是IO密集型,线程进入IO等到时会自动释放GIL索引,因此GIL锁的存在对于这种类型的计算性能影响不算大
2,如果使用多线程,线程的类型基本都是CPU密集型,只能等待解释器不间断运行了1000字节码(Py2)或运行15毫秒(Py3)后,该线程也会放弃GIL,切换到其他的线程执行。

使用线程池来执行线程任务的步骤如下:

  1. 调用 ThreadPoolExecutor 类的构造器创建一个线程池。
  2. 定义一个普通函数作为线程任务。
  3. 调用 ThreadPoolExecutor 对象的 submit() 方法来提交线程任务。
  4. 当不想提交任何任务时,调用 ThreadPoolExecutor 对象的 shutdown() 方法来关闭线程池。
def test(value1, value2=None):
    print("%s threading is printed %s, %s"%(threading.current_thread().name, value1, value2))
    time.sleep(2)
    return 'finished'

def test_result(future):
    print(future.result())

if __name__ == "__main__":
    import numpy as np
    from concurrent.futures import ThreadPoolExecutor
    threadPool = ThreadPoolExecutor(max_workers=4, thread_name_prefix="test_")
    for i in range(0,10):
        future = threadPool.submit(test, i,i+1)

    threadPool.shutdown(wait=True)

更多使用参考PYTHON线程池及其原理和使用(超级详细)

3.2 java常用线程池

常用4中类型的线程池

  • newFixedThreadPool
    构造函数
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
}

从构造方法可以看出,它创建了一个固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大值nThreads。线程池的大小一旦达到最大值后,再有新的任务提交时则放入无界阻塞队列中,等到有线程空闲时,再从队列中取出任务继续执行。

  • newCachedThreadPool
    构造函数
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

从构造方法可以看出,它创建了一个可缓存的线程池。当有新的任务提交时,有空闲线程则直接处理任务,没有空闲线程则创建新的线程处理任务,队列中不储存任务。线程池不对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。如果线程空闲时间超过了60秒就会被回收。(使用方法不是非常推荐)

  • newSingleThreadExecutor
    构造函数
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
}

从构造方法可以看出,它创建了一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行。

  • newScheduledThreadPool
    构造函数
public class OneMoreStudy {
    public static void main(String[] args) {
        final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
        System.out.println("提交时间: " + sdf.format(new Date()));
        scheduledThreadPool.schedule(new Runnable() {
                @Override
                public void run() {
                    System.out.println("运行时间: " + sdf.format(new Date()));
                }
            }, 3, TimeUnit.SECONDS);
        scheduledThreadPool.shutdown();
    }
}

这个方法创建了一个固定大小的线程池,支持定时及周期性任务执行

根据使用习惯选择合适的方法类,更多可以参考Java中常用的四种线程池

3.3 go常用线程池

go的基础方法类中没有实现线程池,需要自己实现,或者引入第三方库进行实现。

4. 疑问和思考

4.1 go语言中,协程的成本已经很低,还有必要使用线程池吗?

梳理常用的开发语言中,是有已经有了现成的线程池方法(类)提供使用,情况如下:

开发语言是否支持线程池备注
python
java
go可以引用第三方的库或者自己实现

go的协程已经把单个协程的成本降低到足够低,还有必要设计线程池吗?该问题在Go Forum 中 skillian 做了解答。

我引用回复

Like lutzhorn said: Need? No.

But for some workloads in some projects, it might make sense to have a general worker pool implementation. The benefit is that the memory consumption can be limited by not allowing the number of goroutines to exceed whatever the pool allows, though I’m unsure of what order of magnitude of goroutines you need before that benefit is manifested.

Francesc Campoy created a fractal with 4 million goroutines (link 55) and it worked and scaled, but not perfectly. The issue wasn’t with the number of goroutines but that the runtime spent more time managing the goroutines than the goroutines actually worked. By giving the goroutines more work, (I think instead of each goroutine processing only one pixel, they processed the whole line?) the solution still scaled and ended up performing better.

翻译过来就是
1, 通常不需要
2, 除了特殊场景,特殊项目上,线程池是有意义的。这样做的好处是,可以通过不允许超过池允许的程序的数量来限制内存消耗,尽管我不确定在显示出这种好处之前需要多少量级的程序。

5. 参考文档

  • 一文快速了解进程、线程与协程
  • 进程、线程以及协程的区别
  • 深入golang之—goroutine并发控制与通信
  • Java线程间的通信

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

智能小程序多语言适配指南

i18n 配置 启用多语言配置&#xff0c;需开启项目配置&#xff08;project.tuya.json&#xff09;中的 {"i18n": true} 选项。多语言的配置内容存放在小程序开发者平台 多语言管理。 本章节的多语言仅适用于智能小程序。如果您开发的是面板小程序&#xff0c;请查阅…

华南理工大学数字信号处理实验实验二源码(薛y老师)

一、实验目的 ▪ 综合运用数字信号处理的理论知识进行信号分析并利用MATLAB作为编程工具进行计算机实现&#xff0c;从而加 深对所学知识的理解&#xff0c;建立概念。 ▪ 掌握数字信号处理的基本概念、基本理论和基本方法。 ▪ 学会用MATLAB对信号进行分析和处理。 ▪ 用F…

QCustomPlot开源库使用

1.简介 QCustomPlot是用于绘图和数据可视化的Qt C 小部件。它没有进一步的依赖关系&#xff0c;并且有据可查。该绘图库专注于制作美观&#xff0c;出版质量的2D绘图&#xff0c;图形和图表&#xff0c;以及为实时可视化应用程序提供高性能。看一下“ 设置”和“ 基本绘图”教…

【系统调用IO】open、close、read、write、lseek

目录 3 系统调用IO3.1 文件描述符3.1.1 FILE结构体3.2.2 文件描述符 3.3 open、close、read、write、lseek3.3.1 文件权限3.3.2 open3.3.3 close3.3.4 read3.3.5 write3.3.6 lseek3.3.7 代码示例 文件io和标准io的区别 橙色 3 系统调用IO 3.1 文件描述符 3.1.1 FILE结构体 …

链表|数据结构|C语言深入学习

什么是链表 离散&#xff0c;就是“分离的、散开的” 链表是什么样子的&#xff1a; 有限个节点离散分配 彼此间通过指针相连 除了首尾节点&#xff0c;每个节点都只有一个前驱节点和一个后继节点 首节点没有前驱结点&#xff0c;尾节点没有后继节点 基本概念术语&#xf…

2023年12月青少年机器人技术等级考试(二级)理论综合试卷

2023年12月青少年机器人技术等级考试&#xff08;二级&#xff09;理论综合试卷 选择题 第 1 题 单选题 下图中&#xff0c;能够将圆周运动转化为往复摆动的是&#xff1f;&#xff08; &#xff09; A. B. C. D. 第 2 题 单选题 如图&#xff0c;该机械结构可实现的运动…

idea远程服务调试

1. 配置idea远程服务调试 这里以 idea 新 ui 为例&#xff0c;首先点击上面的 debug 旁边的三个小圆点&#xff0c;然后在弹出的框框中选择 “Edit”&#xff0c;如下图所示。 然后进入到打开的界面后&#xff0c;点击左上角的 “” 进行添加&#xff0c;找到 “Remote JVM De…

HTML 入门手册(二)

目录 10-表单 11-input标签 11.1文本框 (text) 11.2密码框 (password) 11.3单选按钮 (radio) 11.4复选框 (checkbox) 11.5普通按钮 11.6提交按钮 (submit) 11.7重置按钮 (reset) 11.8隐藏域 (hidden) 11.9文件上传 (file) 11.10数字输入 (number) 11.11日期输入 (…

有效网络安全意识的正确策略

员工在保护组织资产方面发挥着重要作用。随着威胁形势的不断变化&#xff0c;网络安全意识培训是创建良好安全文化的重要组成部分。 为什么要进行网络安全意识培训&#xff1f; 2022 年&#xff0c; 81% 的组织遭受恶意软件、网络钓鱼和密码攻击&#xff0c;主要针对用户。 …

SpringBoot 异常报告器解析

介绍 SpringBootExceptionReporter用于捕获和处理启动期间的异常&#xff0c;例如应用程序上下文的初始化失败。我们业务中的异常处理一般使用拦截器进行拦截处理业务异常。 异常报告流程解析 框架内实现 reportException实现 FailureAnalyzer介绍 analyze逻辑 FailureAnalys…

数据结构·顺序表应用

本节应用是要用顺序表实现一个通讯录&#xff0c;收录联系人的姓名、性别、电话号码、住址、年龄 ​​​​​​​ 顺序表的实现在上一节中已经完成了&#xff0c;本节的任务其实就是应用上节写出来的代码的那些接口函数功能&#xff0c;做出来一个好看的&#xff0c;可…

1.电子基础

https://www.falstad.com/circuit/circuitjs.html vcc&#xff1a;正极 三角形&#xff1a;负极 理想电路原件 电压源、电流源 电压源&#xff1a;正负号表示电压方向 电流源&#xff1a;箭头表示电流方向

2024PMP考试新考纲-【过程领域】近期典型真题和很详细解析(9)

华研荟继续为您分享【过程Process领域】的新考纲下的真题&#xff0c;帮助大家体会和理解新考纲下PMP的考试特点和如何应用所学的知识和常识&#xff08;经验&#xff09;来解题&#xff0c;并且举一反三&#xff0c;一次性3A通过2024年PMP考试。 2024年PMP考试新考纲-【过程领…

Drivable 3D Gaussian Avatars 论文笔记

Drivable 3D Gaussian Avatars 论文笔记 主要的算法架构和贡献是什么&#xff1f;如何使用这个deformation 呢&#xff1f; 主要的算法架构和贡献是什么&#xff1f; 这篇文章主要使用了两个当前流行的概念&#xff0c;一是3D高斯溅射&#xff0c;二是cage-based deformation。…

java SSM政府采购管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM政府采购管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代 码和数据库&#xff0c;系统主要采…

DC-7靶机做题记录

靶机下载地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1w2c_QKd_hOoR2AzNrdZjMg?pwdtdky 提取码&#xff1a;tdky 参考&#xff1a; DC7靶机地址&#xff1a;http://www.five86.com/downloads/DC-7.zipDC7靶场介绍: https://www.vulnhub.com/entry/dc-7,356/…

Matlab/simulink风储调频,多台飞轮储能调频,风电场调频,飞轮储能带有虚拟惯量和下垂控制,三机九节点系统一次调频,离散模型

上述为不同飞轮储能容量配比&#xff0c;风电场容量配比&#xff0c;以及有无附加频率控制的飞轮储能出力分析。 飞轮储能驱动电机为永磁同步机电机PMSG 有无飞轮储能容量较小&#xff0c;所以对频率的改善效果有限&#xff0c;不过可以继续增大容量&#xff0c;从而增大频率的…

数据结构之顺序表的增删查改

别丢了你的勇敢 前言&#xff1a; 自今日起&#xff0c;我们正式越过C语言的大山&#xff0c;走向了数据结构的深山&#xff0c;现如今摆在我们面前的第一个坎就是顺序表&#xff0c;我们需要了解顺序表的定义&#xff0c;并且知道&#xff0c;如何对其进行增删查改&#xff0…

unicloud 云对象 schema

目录 云对象 创建云对象 客户端调用 二、Schema&#xff08;表结构&#xff09; 什么是Schema&#xff1f; 如何编写DB Schema Schema的一级节点 客户端直连数据库 字段属性 字段类型bsonType 默认值defaultValue/forceDefaultValue 云对象 创建云对象 云对象&#…

智能算法 | Matlab实现改进黑猩猩优化算法SLWCHOA与多个基准函数对比与秩和检验

智能算法 | Matlab实现改进黑猩猩优化算法SLWCHOA与多个基准函数对比与秩和检验 目录 智能算法 | Matlab实现改进黑猩猩优化算法SLWCHOA与多个基准函数对比与秩和检验预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab实现改进黑猩猩优化算法SLWCHOA与多个基准函数…