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调度和分派 ...
随机推荐
- [Ethereum] 浅谈加密商品市场 OpenSea 与 opensea-js
OpenSea 是用于交易以太坊加密商品的网上商店,主要的商品是 ERC721.ERC1155 标准的 Token. 它的特色就在于,只需要一个部署好的智能合约,你就能在 OpenSea 提供的界面上 ...
- dotnet 6 已知问题 获取 CultureInfo.NumberFormat 可能抛出 IndexOutOfRangeException 异常
本文记录一个 dotnet 6 已知问题,准确来说这是一个在 dotnet 5 引入的问题,到 dotnet 6.0.12 还没修.在获取 CultureInfo.NumberFormat 属性时,在 ...
- VGA显示文字
VGA显示文字 VGA字符显示的原理 把要显示的字符转换成字符点阵,然后编码存储,着色的部分为1,其它为0.然后在VGA上输出显示. 字符点阵生成软件: https://www.zhetao.com/ ...
- 如何通过前后端交互的方式制作Excel报表
前言 Excel拥有在办公领域最广泛的受众群体,以其强大的数据处理和可视化功能,成了无可替代的工具.它不仅可以呈现数据清晰明了,还能进行数据分析.图表制作和数据透视等操作,为用户提供了全面的数据展示和 ...
- redis rdb数据持久化
面试和工作,持久人都是重点! Redis是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失.所以Redis提供了持久化功能! RDB(Redis ...
- 【项目学习】Anchor:一种提供稳定币存款低波动收益率的去中心化的储蓄协议
简介 基于稳定币的获利产品. 贷方人放出稳定币以供借款.借方通过抵押资产(base asset)的方式,以低于协议定义的借贷比率借入稳定币.Anchor 协议使用抵押资产进行质押以获得奖励,然后将质押 ...
- 从零在win10上测试whisper、faster-whisper、whisperx在CPU和GPU的各自表现情况
Anaconda是什么? Anaconda 是一个开源的 Python 发行版本,主要面向数据科学.机器学习和数据分析等领域.它不仅包含了 Python 解释器本身,更重要的是集成了大量的用于科学计算 ...
- Windows有自带的远程桌面 为啥还要商业远程桌面
网上有一类观点:最好的远程桌面就是windows自带的远程桌面. 那我们打破砂锅问到底,亲手实践下看看. 首先,我们来到了windows官网-远程桌面介绍页面. 如何使用远程桌面 设置你想要连接以使其 ...
- C语言:如何让printf输出更加美化(用游戏英雄属性作例子)
#include <stdlib.h> /* run this program using the console pauser or add your own getch, system ...
- 用 C 语言开发一门编程语言 — 基于 Lambda 表达式的函数设计
目录 文章目录 目录 前文列表 函数 Lambda 表达式 函数设计 函数的存储 实现 Lambda 函数 函数的运行环境 函数调用 可变长的函数参数 源代码 前文列表 <用 C 语言开发一门编 ...