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 ...
随机推荐
- Mongoid Paging and Iterating Over Large Collections
遍历数据库中的所有记录时,我们首先想到的是Model.all.each.但是,当数据量很大的时候(数万?),这就不怎么合适了,因为Model.all.each会一次性加载所有记录,并将其实例化成 Mo ...
- 《Cracking the Coding Interview》——第14章:Java——题目5
2014-04-26 19:06 题目:Java中的对象反射机制是什么?有鼠么用? 解法:完全不了解,因为java编程经验太少,完全没用过.查了一些资料后,感觉反射机制是个强大并需要边用边体会的强大工 ...
- Percona-Tookit工具包之pt-summary
Preface As a dba,We are obliged to master several basic tools(such as vmstat,top,netstat,ios ...
- Android数据储存之SharedPreferences总结
写在前面:本文是我参考李刚老师的<疯狂Android讲义>以及API所写的读书笔记,在此表示感谢,本人小白,如有错误敬请指教. SharedPreferences的使用背景: 有时候,应用 ...
- [译]如何去除pandas dataframe里面的Unnamed的列?
原文来源: https://stackoverflow.com/questions/43983622/remove-unnamed-columns-in-pandas-dataframe 问:我有一个 ...
- quagga源码学习--BGP协议中的routemap
路由策略的基础知识 定义 路由策略(Routing Policy)作用于路由,主要实现了路由过滤和路由属性设置等功能,它通过改变路由属性(包括可达性)来改变网络流量所经过的路径. 目的 路由器在发布. ...
- POJ A-Wireless Network
http://poj.org/problem?id=2236 An earthquake takes place in Southeast Asia. The ACM (Asia Cooperated ...
- BI商业智能培训系列——(三)SSAS入门
简介: 一个客户端工具,提供了设计.创建和管理来自数据仓库的多维数据集的功能,还提供对OLAP数据客户端访问功能. 解决方案如下: 相关概念: 维度: 维度可以理解为划分依据,简单的说就是看问 ...
- CocoaPods 安装与使用
1.如果之前已经安装过的 gem list --local | grep cocoapods 会看到如下输出: cocoapods (1.1.1)cocoapods-deintegrate (1.0. ...
- linux sed讲解
1.sed 查找替换 显示某一行或某几行##替换sed 's###g' oldboy.txtsed 's@@@g' oldboy.txt sed -i 's###g' oldboy.txtsed -i ...