10-Python进程与线程
Python进程
创建新进程
from multiprocessing import Process
import time
def run_proc(name): #子进程要执行的代码
for i in range(5): #每秒输出一个name
print(name)
time.sleep(1)
if __name__ == '__main__':
p = Process(target=run_proc, args=('test',)) #创建进程
p.start() #开始进程
print('Child process end.')
解释:
- 在Python中,通过函数来定义一个进程
- target指定函数名,即要运行的进程
- args指定函数参数
- 主进程运行完后,子进程并不会结束。
- 创建子进程需要在main中进行
守护进程
守护进程:父进程退出后,子进程也退出。
from multiprocessing import Process
import time
def run_proc(name): #子进程要执行的代码
for i in range(5): #每秒输出一个name
print(name)
time.sleep(1)
if __name__ == '__main__':
p = Process(target=run_proc, args=('test',)) #创建进程
p.daemon = True #设置为守护进程
p.start() #开始进程
time.sleep(3)
print('Child process end.')
结果:
test
test
test
Child process end
解释:
- 只输出了3次test,而不是5次。
- 因为3s后,主进程退出;主进程结束后,子进程也结束。
- p.daemon=True需要在start()之前运行。(一旦开始运行,就来不及设置为守护进程了)
join()方法
join():暂停运行当前进程,直到子进程运行完。
from multiprocessing import Process
import time
def run_proc(name): #子进程要执行的代码
for i in range(5): #每秒输出一个name
print(name)
time.sleep(1)
if __name__ == '__main__':
p = Process(target=run_proc, args=('test',)) #创建进程
p.start() #开始进程
p.join() #等待子进程运行完
print('Child process end.')
结果:
test
test
test
test
test
Child process end.
解释:
- 一旦调用join(),主进程就暂停运行。
- 等待p进程运行完,主进程继续运行。
terminate()方法
terminate():立即停止该进程
from multiprocessing import Process
import time
def run_proc(name): #子进程要执行的代码
for i in range(5): #每秒输出一个name
print(name)
time.sleep(1)
if __name__ == '__main__':
p = Process(target=run_proc, args=('test',)) #创建进程
p.start() #开始进程
time.sleep(2)
p.terminate() #2s后停止运行p进程
print('Child process end.')
进程池
进程池可以同时管理多个子进程
from multiprocessing import Pool
def run_proc(name): #子进程要执行的代码
print(name)
if __name__ == '__main__':
p = Pool(4) #最多同时执行4个进程(一般为CPU核数),有进程运行完腾出的空间再分配给其他进程运行
for i in range(5):
p.apply_async(run_proc, args=(i,)) #在进程池中添加进程
p.close() #执行join()前必须执行close(),表示不能继续添加新的Process了
p.join() #等待子进程结束再往下执行
解释:
例子中使用了join方法,等待进程中的子进程运行完,再接着往下运行,如果子进程陷入死循环,那主进程就会一直等待。
使用p.terminate()方法,结束进程池中的所有进程,可以防止子进程成为僵尸进程。
p = Pool(4)表示最多同时执行4个进程
若往进程池中添加第5个进程(假设此时前4个进程没有运行完) ,那第5个进程不会立即运行。前4个进程中,其中一个进程运行完后,会腾出一个位置,第5个进程替补,所以最多同时运行4个进程。
subprocess
subprocess使得子进程可以调用另一个程序,并提供输入输出操作。
例1:
import subprocess
ret = subprocess.call(['ping', 'www.python.org']) #执行命令,返回状态码
print('Exit code:', ret) #输出状态码
结果:
正在 Ping dualstack.python.map.fastly.net [151.101.76.223] 具有 32 字节的数据:
来自 151.101.76.223 的回复: 字节=32 时间=102ms TTL=48
来自 151.101.76.223 的回复: 字节=32 时间=106ms TTL=49
来自 151.101.76.223 的回复: 字节=32 时间=105ms TTL=49
来自 151.101.76.223 的回复: 字节=32 时间=104ms TTL=48
151.101.76.223 的 Ping 统计信息:
数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
最短 = 102ms,最长 = 106ms,平均 = 104ms
Exit code: 0
例2-在子进程中交互式输入:
import subprocess
#执行命令
p = subprocess.Popen(['python'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
#输入
output, err = p.communicate(b'print("hello")\nquit()') #输入print "hello"和quit()
print(output.decode('utf-8'))
print('Exit code:', p.returncode) #输出退出码
结果:
hello
Exit code: 0
解释:
- 如果子进程运行错误,错误将会保存在err中
进程间通信
生产者和消费者:
from multiprocessing import Process, Queue
import os, time, random
def write(q): #写进程
for value in ['A', 'B', 'C']:
print('Put %s to queue...' % value)
q.put(value) #写入value
time.sleep(random.random())
def read(q): #读进程
while True:
value = q.get(True) #读出value
print('Get %s from queue.' % value)
if __name__ == '__main__':
q = Queue() #创建Queue
pw = Process(target=write, args=(q,)) #创建写进程,传入队列q
pr = Process(target=read, args=(q,)) #创建读进程,写入队列q
pw.start()
pr.start()
pw.join()
pr.terminate() #pr进程里是死循环,无法等待其结束,只能强行终止
解释:
- Queue队列可以用来进程间通信。
- put函数:往队列里写内容。
- get函数:读出队列中的元素。
结果:
Put A to queue...
Get A from queue.
Put B to queue...
Get B from queue.
Put C to queue...
Get C from queue.
Python多线程
- 进程运行在操作系统中,由操作系统进行管理。
- 线程运行在Python虚拟机(解释器)中,由Python进行管理。
- 对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同时只有一个线程在运行,保证共享内存的数据正确。
- 所以Python多线程是假的多线程,同一时间,只有一个线程在运行,并不能有效利用CPU多核。
- 综上,Python多线程适合IO密集型操作,如爬虫(时间都浪费在了等待Response)。尽量避免使用Python做CPU密集型操作。
创建多线程
import threading
def func1(name):
for i in range(5):
print(name,i)
t1=threading.Thread(target=func1,args=("t1",)) #创建线程,执行函数func1,参数为"t1",注意逗号
t1.start() #启动线程
t1.join() #等待t1线程执行完
threading中其他函数:
threading.current_thread() #返回当前线程信息
threading.active_count() #返回当前活动的线程数(未运行完的线程)
t1.setDaemon(True) #将t设为守护线程(在线程start()之前),主线程结束后守护线程不再运行
线程之间共享数据
示例:
import threading
a=0
def func1():
global a
for i in range(500):
a+=1
t1=threading.Thread(target=func1)
t2=threading.Thread(target=func1)
t1.start() #启动线程
t2.start() #启动线程
t1.join() #等待t1线程执行完
t2.join() #等待t2线程执行完
print(a)
结果:
1000
解释:
- global声明了a是全局变量,而不是局部变量。
- 但这种方式是不安全的。
为什么不安全:
import threading
balance = 0
def run_thread(n):
for i in range(1000000):
global balance
balance = balance + n #注意这里!!!
balance = balance - n #注意这里!!!
t1 = threading.Thread(target = run_thread , args = (5,))
t2 = threading.Thread(target = run_thread ,args = (8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance) #结果很可能不是0
多线程-锁
给共享变量上一把锁,保证数据的正确,解决上例中的问题。
import threading
balance = 0
lock = threading.Lock() #创建一个锁
def run_thread(n):
for i in range(100000):
lock.acquire() #先要获取锁
try: #放心地改吧
global balance
balance = balance + n
balance = balance - n
finally:
lock.release() #改完了一定要释放锁
t1 = threading.Thread(target = run_thread , args = (5,))
t2 = threading.Thread(target = run_thread ,args = (8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance) #结果为0
Python协程
进程:由操作系统管理。
线程:由Python虚拟机(解释器)管理。
协程:由程序本身管理,所以开销比线程更小。
协程的定义:
协程,又称微线程,纤程。英文名Coroutine。
协程的优点:
不是真正的并发,所以共享数据无需加锁。
由程序本身控制,代码间的切换开销极小。
协程的缺点:
无法利用多核资源。因为协程的本质是单线程,只在单个核上运行。但可以与进程配合解决这个问题。
协程的原理:
协程通过yield实现,yield可以模拟程序的中断,进而切换到另一个程序。
协程并不是真正的并发,而是通过yield模拟中断去执行另一个函数。
在Python中使用协程并不需要我们自己使用yield,调用模块即可
greenlet模块
greenlet是一个用C实现的协程模块。
通过设置.switch()可以实现任意函数之间的切换。
缺点:这种切换属于手动切换,当遇到IO操作时,程序会阻塞,而不能自动进行切换。
安装:
pip install greenlet
示例:
from greenlet import greenlet
import time
def test1():
print("running test1")
gr2.switch() #切换到test2
print("running test1 again ")
time.sleep(2)
gr2.switch()
def test2():
print("running test2")
gr1.switch() #切换到test1
print("running test2 again")
gr1 = greenlet(test1) #实例化一个协程
gr2 = greenlet(test2) #实例化另一个协程
gr1.switch() #执行gr1
gevent模块
Gevent遇到IO操作时,会进行自动切换,属于主动式切换。
安装:
pip install gevent
示例:
import gevent, time
def func1():
print('主人来电话啦...')
gevent.sleep(3)
print(' 主人那家伙又来电话啦...')
def func2():
print('打个电话...')
gevent.sleep(2)
print('咦,电话给挂了,接着打...')
def func3():
print("哈哈哈哈")
gevent.sleep(0)
print("嘿嘿嘿....")
start_time = time.time()
gevent.joinall([
gevent.spawn(func2), #生成一个协程
gevent.spawn(func1),
gevent.spawn(func3),
])
print("running time:", (time.time() - start_time))
结果:
打个电话...
主人来电话啦...
哈哈哈哈
嘿嘿嘿....
咦,电话给挂了,接着打...
主人那家伙又来电话啦...
running time: 3.0478341579437256
解释:
可以看出gevent模块切换的效果,遇到IO操作时会自动进行切换,会不停的进行循环遍历,直到切换到已经完成IO操作的协程上去。
10-Python进程与线程的更多相关文章
- python 进程和线程
python中的进程.线程(threading.multiprocessing.Queue.subprocess) Python中的进程与线程 学习知识,我们不但要知其然,还是知其所以然.你做到了你就 ...
- Python进程、线程、协程
进程和线程的解释 进程(process)和线程(thread)是操作系统的基本概念,计算机的核心是CPU,它承担了所有的计算任务: 单个CPU一次只能运行一个任务,代表单个CPU总是运行一个进程,其他 ...
- python进程、线程、协程(转载)
python 线程与进程简介 进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CPU是计算机的核心,它承担计算机的所有任务. 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资 ...
- Python进程和线程
引入进程和线程的概念及区别 1.线程的基本概念 概念 线程是进程中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但 ...
- Python进程、线程、协程详解
进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CPU是计算机的核心,它承担计算机的所有任务. 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资源的管理和分配.任务的调度. ...
- python——进程、线程、协程
Python线程 Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #!/usr/bin/env pytho ...
- Python进程与线程
进程与线程:*进程: 进程是系统中程序执行和资源分配的基本单元, 每个进程都有自己的数据段(存储数据).代码段(存储代码).堆栈段(对象和变量). # 全局变量等资源在多个进程中不能 ...
- python进阶:Python进程、线程、队列、生产者/消费者模式、协程
一.进程和线程的基本理解 1.进程 程序是由指令和数据组成的,编译为二进制格式后在硬盘存储,程序启动的过程是将二进制数据加载进内存,这个启动了的程序就称作进程(可简单理解为进行中的程序).例如打开一个 ...
- python 进程和线程(代码知识部分)
二.代码知识部分 一 multiprocessing模块介绍: python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情 ...
- [ Python - 14 ] python进程及线程编程
什么是进程: 简单来讲,进程就是操作系统中运行的程序或任务,进程和程序的区别在于进程是动态的,而程序是静态的.进程是操作系统资源管理的最小单位. 什么是线程: 线程是进程的一个实体,是cpu调度和分派 ...
随机推荐
- 推荐一个使用 HardLink 硬链接减少重复文件占用磁盘空间的工具
在 NTFS 文件系统里面,咱可以使用 HardLink 硬链接的方式,将多个重复的文件链接到磁盘的同一份记录里面,从而减少在磁盘里面对重复文件存储多份记录,减少磁盘空间的占用.本文将和大家推荐我所做 ...
- Windows 官方提供的触屏硬件延迟测量方法
本文记录微软 Windows 官方在 Windows Hardware Lab Kit 提供的触屏硬件延迟测量方法 Overview of measuring Touch Down Hardware ...
- 基于Jenkins+k8s+Git等技术构建DeOps平台
一.DeOps简介 1.什么是DeOps? 1.1 敏捷开发 提高开发效率,及时跟进用户需求,缩短开发周期. 敏捷开发包括编写代码和构建代码两个阶段,可以使用 git 或者 svn 来管理代码,用 ...
- easyExcel多行表头设定不同样式和特定单元格设定样式的实现
前言 有个需求,需要设置Excel导出的样式,样式如下图所示,有三个表头行,第一个表头行需要加粗和灰色背景,另外两个表头行使用另外的样式,并且当测试结果单元格出现x或者未通过的时候,设置其为红色字体. ...
- HouseParty原创故事全角色关系及主线剧情介绍(最新版)
这是原创故事的主要的角色的主线及支线剧情的介绍及攻略和注意事项等. 这里的图比哔哩哔哩上的图清楚一点,哔哩哔哩同号:宅猫君007 以上是全角色的关系图 最新版本的游戏下载就在我的网站上:https:/ ...
- LMDeploy量化部署LLM&LVM实操-书生浦语大模型实战营第二期第5节作业
书生浦语大模型实战营第二期第5节作业 本页面包括实战营第二期第五节作业的全部操作步骤.如果需要知道模型量化部署的相关知识请访问学习笔记. 作业要求 基础作业 完成以下任务,并将实现过程记录截图: 配置 ...
- Linux中的touch命令
Linux中一个文件有3种时间属性,分别是mtime,ctime,atime: modification time (mtime) 当该文件的『内容数据』变更时,就会升级这个时间!内容数据指的是文件的 ...
- SQL如何删除所有字段都相同的重复数据?
SQL Server数据库:有时候在处理数据时会遇到不加主键的表,导致数据表内出现了一模一样的数据,刚开始第一时间想到的方式是,把两条数据全部删除,然后再插入一条,但是这种可能数据量比较少的话,还可以 ...
- DP-Modeler三维修模软件简介
图像快速建模系统DP-Modeler是天际航自主研发的一款集精细化单体建模及Mesh网格模型修饰于一体的新型软件.通过特有的摄影测量算法,支持航测摄影.无人机影像.地面影像.车载影像.激光点云等多数据 ...
- HEOI 2024游记
(虽然是体验罢 HEOI2024游记 前 \(\,\,\)省选! \(\,\,\)得知省选可以报名体验的时候着实很兴奋 也是不假思索的就报了 Day 0 本来说是九点上大巴 结果硬生拖到十点半(恼 分 ...