Python的并发并行[0] -> 基本概念
基本概念 / Basic Concept
快速跳转
- 进程 / Process
- 线程 / Thread
- 协程 / Coroutine
- 全局解释器锁 / Global Interpreter Lock
- 守护线程 / Daemon Thread
- 信号量 / Semaphore
- 有界信号量 / BoundedSemaphore
- 同步原语 / Synchronization Primitive
- 锁 / Lock
- 互斥锁和可重入锁 / Mutex Lock and Reentrant Lock
- 死锁 / Deadlock
- 线程安全 / Thread Safety
0 简介与动机 / Why Multi-Thread/Multi-Process/Coroutine
在多线程(multithreaded, MT)编程出现之前,计算机程序的执行是由单个步骤序列组成的,该序列在主机的CPU中按照同步顺序执行。即无论任务多少,是否包含子任务,都要按照顺序方式进行。
然而,假定子任务之间相互独立,没有因果关系,若能使这些独立的任务同时运行,则这种并行处理方式可以显著提高整个任务的性能,这便是多线程编程。
而对于Python而言,虽然受限于GIL(全局解释器锁)的控制,在处理计算密集型程序时,多线程可能并不能提升性能,但对于I/O密集型的程序来说,Python的多线程模式就能很好的起到性能提升的作用。
1 相关名词 / Relevant Noun
1.0 进程 / Process
是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体,是一个执行中的程序,也被称为重量级进程。
1.1 线程 / Thread
线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
1.2 协程 / Coroutine
协程是在一个线程执行过程中可以在一个子程序的预定或者随机位置中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。它本身是一种特殊的子程序或者称作函数。
一个程序可以包含多个协程,可以对比与一个进程包含多个线程。我们知道多个线程相对独立,有自己的上下文,切换受系统控制;而协程也相对独立,有自己的上下文,但是其切换由自己控制,由当前协程切换到其他协程由当前协程来控制。
1.3 全局解释器锁 / Global Interpreter Lock
全局解释器锁GIL是计算机程序设计语言解释器用于同步线程的工具,使得解释器任何时刻仅有一个线程在执行。常见例子有CPython(JPython不使用GIL)与Ruby MRI。
正式由于全局解释器锁的存在,使得Python解释器在任意时刻只能以单线程的形式运行,即Python中的多线程实际上是对多线程中的每个线程执行一定内存数量的程序后,切换到另一个线程继续执行,直到再次切回继续执行。因此Python应对CPU-bound Computation时难以体现优势,而处理类似爬虫等I/O-intensive Computation时则有较大优势。
1.4 守护线程 / Daemon Thread
所谓守护线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。即守护线程的存在不影响程序退出。
用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的离开,如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。
1.5 信号量 / Semaphore
Semaphore是最古老的同步原语之一,由荷兰计算机科学家 Edsger W. Dijkstra 发明。(他最早使用名为P()和V()的函数对应acquire()和release())。threading模块中,Semaphore在内部管理着一个计数器。调用acquire()会使这个计数器-1,release()则是+1。计数器的值永远不会小于 0,当计数器到0时,再调用acquire()就会阻塞,直到其他线程来调用release()。Semaphore 也支持上下文管理协议。
1.6 有界信号量 / BoundedSemaphore
threading模块中的一个工厂函数,返回一个新的有界信号量对象。一个有界信号量会确保它当前的值不超过它的初始值。如果超过,则引发ValueError。在大部分情况下,信号量用于守护有限容量的资源。如果信号量被释放太多次,它是一种有bug的迹象。如果没有给出,value默认为1。
1.7 同步原语 / Synchronization Primitive
当一个进程调用一个send原语时,在消息开始发送后,发送进程便处于阻塞状态,直至消息完全发送完毕,send原语的后继语句才能继续执行。当一个进程调用一个receive原语时,并不立即返回控制,而是等到把消息实际接收下来,并把它放入指定的接收区,才返回控制,继续执行该原语的后继指令。在这段时间它一直处于阻塞状态。上述的send和receive被称为同步通信原语或阻塞通信原语。事件作为一种同步原语,是计算机科学中的一种同步机制,用来指示等待中的进程特定条件已经变为真。
1.8 锁 / Lock
对于锁来说,其实锁的本质是一个线程之间约定的控制权,即锁的作用实质上并不能将某一资源进行锁定,使其他线程无法修改。锁的本质在于,多个线程之间对某一个锁进行约定,约定这把锁对应的公共资源,当需要对这把锁对应的资源进行修改时,必须拥有这把锁的权限才能进行。因此,当需要修改某资源时,就需要尝试获取对应的锁,而当这把锁的权限被其他线程获取时,其余需要获取的线程就会进入阻塞等待状态。且当锁释放时,权限的获取是随机的,不论进入阻塞的时间先后。
1.9 互斥锁和可重入锁 / Mutex Lock and Reentrant Lock
对于互斥锁和可重用锁,互斥锁只能被获取一次,若多次获取则会产生阻塞,需等待原锁释放后才能再次入锁。而可重入锁则可被本线程多次acquire入锁,但是要求入锁次数与释放次数相同,才能完全解锁,且锁的释放需要在同一个线程中进行
Note: 对于可重入锁来说,可多次入锁的特性仅在本线程有效,也就是说,即使是可重入锁,被一个线程获取锁定时,其他线程无法再次进入,只有本线程可以。
1.10 死锁 / Deadlock
死锁出现在一个资源被多次调用,而调用方均未能释放资源,便会造成死锁现象。死锁大致可分为两种形式出现,迭代死锁和相互调用死锁。一般死锁是有互斥锁造成的,使用可重入锁则可以避免部分死锁问题。
1.11 线程安全 / Thread Safety
线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。 线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。
参考链接
《Python 核心编程 第三版》
Python的并发并行[0] -> 基本概念的更多相关文章
- Python的并发并行[3] -> 进程[0] -> subprocess 模块
subprocess 模块 0 模块描述 / Module Description From subprocess module: """Subprocesses wit ...
- Python的并发并行[1] -> 线程[0] -> threading 模块
threading模块 / threading Module 1 常量 / Constants Pass 2 函数 / Function 2.1 setprofile()函数 函数调用: thread ...
- Python的并发并行[2] -> 队列[0] -> queue 模块
queue 模块 / queue Module 1 常量 / Constants Pass 2 函数 / Function Pass 3 类 / Class 3.1 Queue类 类实例化:queue ...
- Python的并发并行[4] -> 并发[0] -> 利用线程池启动线程
利用线程池启动线程 submit与map启动线程 利用两种方式分别启动线程,同时利用with上下文管理来对线程池进行控制 from concurrent.futures import ThreadPo ...
- Python的并发并行[1] -> 线程[1] -> 多线程的建立与使用
多线程的建立与使用 目录 生成线程的三种方法 单线程与多线程对比 守护线程的设置 1 生成线程的三种方法 三种方式分别为: 创建一个Thread实例,传给它一个函数 创建一个Thread实例,传给它一 ...
- Python的并发并行[1] -> 线程[2] -> 锁与信号量
锁与信号量 目录 添加线程锁 锁的本质 互斥锁与可重入锁 死锁的产生 锁的上下文管理 信号量与有界信号量 1 添加线程锁 由于多线程对资源的抢占顺序不同,可能会产生冲突,通过添加线程锁来对共有资源进行 ...
- Python的并发并行[1] -> 线程[3] -> 多线程的同步控制
多线程的控制方式 目录 唤醒单个线程等待 唤醒多个线程等待 条件函数等待 事件触发标志 函数延迟启动 设置线程障碍 1 唤醒单个线程等待 Condition类相当于一把高级的锁,可以进行一些复杂的线程 ...
- Python的并发并行[2] -> 队列[1] -> 使用队列进行任务控制
使用队列进行任务控制 1 FIFO与LIFO队列 FIFO(First In First Out)与LIFO(Last In First Out)分别是两种队列形式,在FIFO中,满足先入先出的队列方 ...
- Python的并发并行[3] -> 进程[1] -> 多进程的基本使用
多进程的基本使用 1 subprocess 常用函数示例 首先定义一个子进程调用的程序,用于打印一个输出语句,并获取命令行参数 import sys print('Called_Function.py ...
随机推荐
- app分享功能开发
最近在开发一个社交平台的app需要用到分享功能,本来想自己开发的,在网上花了很长时间查了很多教程结果却不尽人意,无意中看到还有类似的开源组件友推,结合自己的开发经验,把一些集成步骤和问题整理成文档奉献 ...
- 【 Logistic Regression 】林轩田机器学习基石
这里提出Logistic Regression的角度是Soft Binary Classification.输出限定在0~1之间,用于表示可能发生positive的概率. 具体的做法是在Linear ...
- asp.net 身份验证-Form 身份验证
一. .net身份验证简介 1.身份验证就是检测用户是否登录及所访问的资源是否有权限.当我们在访问一个受保护网络资源时,往往需要输入用户名.密码信息,或通过其他证书.第三方身份验证等方式.验证(Aut ...
- Python 实现随机打乱字符串
from random import shuffle def shuffle_str(s): # 将字符串转换成列表 str_list = list(s) # 调用random模块的shuffle函数 ...
- python2.7写入文件时指定编码为utf-8
python3.0可以这样写 f = open('ufile.log', 'w', 'utf-8') 但在python2.7中open()没有编码参数,如上那样写会报错,可以使用如下模块 impo ...
- jvm可视化工具jvisualvm插件——Visual GC
转自:http://blog.csdn.net/xuelinmei_happy/article/details/51090115 Visual GC是一个Java 内存使用分析与GC收集的可视化工具插 ...
- Leetcode 664.奇怪的打印机
奇怪的打印机 有台奇怪的打印机有以下两个特殊要求: 打印机每次只能打印同一个字符序列. 每次可以在任意起始和结束位置打印新字符,并且会覆盖掉原来已有的字符. 给定一个只包含小写英文字母的字符串,你的任 ...
- hadoop-eclipse环境搭建(二)
Eclipse插件配置 第一步:把我们的"hadoop-eclipse-plugin-1.0.0.jar"放到Eclipse的目录的"plugins"中,然后重 ...
- # ML学习小笔记—Linear Regression
Regression Output a scalar Model:a set of function 以Linear model为例 y = b+w * $x_cp$ parameters:b,W f ...
- 【干货】2个小时教你hexo博客添加评论、打赏、RSS等功能 (转)
备注:该教程基于Hexo 2.x版本,目前Hexo是3.x版本,照本教程实现有可能会出现404错误,笔者目前还未找时间去解决,待笔者找时间解决该问题后,再写一篇该问题的解决教程,给各位读者带来困扰,还 ...