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进程与线程的更多相关文章

  1. python 进程和线程

    python中的进程.线程(threading.multiprocessing.Queue.subprocess) Python中的进程与线程 学习知识,我们不但要知其然,还是知其所以然.你做到了你就 ...

  2. Python进程、线程、协程

    进程和线程的解释 进程(process)和线程(thread)是操作系统的基本概念,计算机的核心是CPU,它承担了所有的计算任务: 单个CPU一次只能运行一个任务,代表单个CPU总是运行一个进程,其他 ...

  3. python进程、线程、协程(转载)

    python 线程与进程简介 进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CPU是计算机的核心,它承担计算机的所有任务. 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资 ...

  4. Python进程和线程

    引入进程和线程的概念及区别 1.线程的基本概念 概念 线程是进程中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但 ...

  5. Python进程、线程、协程详解

    进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CPU是计算机的核心,它承担计算机的所有任务. 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资源的管理和分配.任务的调度. ...

  6. python——进程、线程、协程

    Python线程 Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #!/usr/bin/env pytho ...

  7. Python进程与线程

    进程与线程:*进程: 进程是系统中程序执行和资源分配的基本单元, 每个进程都有自己的数据段(存储数据).代码段(存储代码).堆栈段(对象和变量). # 全局变量等资源在多个进程中不能          ...

  8. python进阶:Python进程、线程、队列、生产者/消费者模式、协程

    一.进程和线程的基本理解 1.进程 程序是由指令和数据组成的,编译为二进制格式后在硬盘存储,程序启动的过程是将二进制数据加载进内存,这个启动了的程序就称作进程(可简单理解为进行中的程序).例如打开一个 ...

  9. python 进程和线程(代码知识部分)

    二.代码知识部分 一 multiprocessing模块介绍: python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情 ...

  10. [ Python - 14 ] python进程及线程编程

    什么是进程: 简单来讲,进程就是操作系统中运行的程序或任务,进程和程序的区别在于进程是动态的,而程序是静态的.进程是操作系统资源管理的最小单位. 什么是线程: 线程是进程的一个实体,是cpu调度和分派 ...

随机推荐

  1. Multisim仿真验证之二极管的特性参数

    二极管的特性 正向 R1 10% 20% 30% 50% 70% 90% Vd/mV 299 543 583 608 627 658 Id/mA 0.01 0.1 0.6 1.4 2.8 7.2 rd ...

  2. 使用AI在原神里自动钓鱼,扫描Git仓库泄露的密码 【蛮三刀酱的Github周刊第三期】

    大家好,这里是的Github精彩项目分享周刊,我是每周都在搬砖的蛮三刀酱. 我会从Github热门趋势榜里选出 高质量.有趣,牛B 的开源项目进行分享. 榜单取自实时Github Trending排行 ...

  3. Solution Set - 线段树

    A[洛谷P5787]无向图,每条边有一个出现时段,求每个单位时段图是否是二分图. B[洛谷P5227]无向图,询问删除一个边集后是否连通. C[洛谷P3733]连通无向图,边有权,加边.修改(增加的边 ...

  4. 7、yum 仓库服务与 PXE 网络装机

    1.部署 yum 软件仓库 1.1.使用本地 yum 挂载 umount /dev/sr0 mount /dev/sr0 /media/mkdir /root/yum.bakmv /etc/yum.r ...

  5. 基于 three.js 加载器分别加载模型

    点击查看代码 /** * 参数:模型文件路径,成功回调函数 * * 基于 three.js 加载器分别加载模型 * * 全部加载后通过回调函数传出打印 */ import { FBXLoader } ...

  6. C#TMS系统学习(BaseCity页面)

    C#TMS系统代码-基础页面BaseCity学习 本人纯新手,刚进公司跟领导报道,我说我是java全栈,他问我会不会C#,我说大学学过,他说这个TMS系统就给你来管了.外包已经把代码给我了,这几天先把 ...

  7. 密码学—凯撒密码Python程序

    文章目录 凯撒密码来源 加密算法 解密算法 凯撒密码来源 古典密码,且属于单表加密 凯撒密码又称恺撒变换.变换加密,凯撒是当时罗马共和时期恺撒的名字命名的,当年恺撒曾用此方法与其将军们进行联系.(当然 ...

  8. 13-flask博客项目之restful api详解1-概念

    一 传统的开发模式 前后端分类概念 前端只需要独立编写客户端代码,后端也只需要独立编写服务端代码提供数据接口即可前端通过AJAX请求来访问后端的数据接口,将Model展示到View中即可 前后端开发者 ...

  9. 全网首一份!你最需要的PPTP MS-CHAP V2 挑战响应编程模拟计算教程!代码基于RFC2759,附全部源码!

    本文基于网络密码课上的实验 本来想水一水就过去,代码就网上找找,不行就GPT写,但是!一份都找不到,找到的代码都是跑不了的,总会是就是乱七八糟.所以准备认真的写一份. 代码编译成功的前提是要预先装好o ...

  10. JavaScript的数学计算库:decimal.js

    An arbitrary-precision Decimal type for JavaScript. 功能 整数和浮点数 简单但功能齐全的 API 复制 JavaScript 和对象的许多方法Num ...