1、基本概念
1.1 进程概念
进程是指一个程序在给定数据集合上的一次执行过程,简单理解为操作系统中正在执行的程序。
- 每个进程都有自己独立的地址空间、内存、数据栈
- 操作系统自动管理进程,分配执行时间
- 进程可以派生子进程
- 进程之间可以相互通信
1.2线程概念
线程是进程的实体,是CPU调度和分配的基本单元,线程必须依托进程存活,线程无法给予公平执行时间,会被其他线程抢占
1.3二者关系
- 线程必须在进程中执行
- 一个进程可以包含多个线程,有且只有一个主线程
- 多线程共享同个地址空间、打开的文件以及其他资源
- 多进程共享物理内存、磁盘等其他资源
2、进程
2.1创建进程
创建进程可以通过os.fork(Linux操作系统),multiprocessing,pool进程池
from multiprocessing import Pool
def text(msg):
for i in range(3):
print("(" + str(msg) + "):" + str(i))
if __name__ == "__main__":
# 创建一个线程池,最大进程数为3
pool = Pool(3)
for i in range(10):
# 向进程池中添加任务
pool.apply_async(text, (i, ))
print("---start---")
# 关闭进程池,表示不能在往进程池中添加进程
pool.close()
# 等待进程池中的所有进程执行结束
pool.join()
print("--- end ---")
2.2进程间通信
multiprocessing提供的Queue模块可以完成进程间通信
from multiprocessing import Process,Queue
import time
#创建子进程,向队列写入数据
def write_task(q):
if not q.full():
for i in range(5):
message="消息"+str(i)
q.put(message)
print('写入{}'.format(message))
#从队列读取数据
def read_task(q):
time.sleep(1)
while not q.empty():
print("读取%s"%q.get(True,2))
if __name__=='__main__':
print('-----父进程开始------')
q=Queue() #实例化一个队列
pw=Process(target=write_task,args=(q,)) #队列作为参数传递给函数
pr=Process(target=read_task,args=(q,))
pw.start()
pr.start()
pw.join()
pr.join()
print("父进程结束")
3、线程
3.1创建线程
python3采用threading来创建线程
3.2线程间通信
线程之间共享数据资源,不需要通过其他方式进行通信
3.3互斥锁
线程可以对共享资源进行随意修改,会导致线程间数据调用混乱,简单的方法就是加锁,先到的线程上锁,后到的排队,等锁打开再进行
互斥锁(mutex)防止多线程同时读写某一内存区域,互斥锁引入一个状态:锁定和非锁定,互斥锁保证每一次只有一个线程进程读写操作
from threading import Thread,Lock
import time
n=100
def task():
global n
mutex.acquire() #上锁
temp=n #赋值给临时变量
time.sleep(0.1)
n=temp-1 #数量减1
print("购买成功,剩余%d张电影票"%n)
mutex.release() #释放锁
if __name__=='__main__':
mutex=Lock() #实例化Lock类
t_l=[] #初始化一个列表
for i in range(10):
t=Thread(target=task) #实例化线程类
t_l.append(t) #将线程实例存入列表中
t.start() #创建线程
for t in t_l:
t.join() #等待子线程结束
3.4消息队列在线程间通信
使用queue模块的Queue,在线程间通信,通常用于生产者和消费者模式。生产数据的模块成为生产者,处理数据的模块成为消费者,生产者与消费者之间的缓冲区称为仓库。
from queue import Queue
import threading,time,random
class Producer(threading.Thread):
def __init__(self,name,queue):
threading.Thread.__init__(self,name=name)
self.data=queue
def run(self) :
for i in range(5):
print("生产者%s将产品%d加入队列!"%(self.getName(),i))
self.data.put(i)
time.sleep(random.random())
print("生产者%s完成!"%self.getName())
class Consumer(threading.Thread):
def __init__(self,name,queue):
threading.Thread.__init__(self, name=name)
self.data = queue
def run(self):
for i in range(5):
val=self.data.get()
print("消费者%s将产品%d取出队列!" % (self.getName(), val))
time.sleep(random.random())
print("消费者%s完成!" % self.getName())
if __name__=='__main__':
print("主线程开始")
queue=Queue()
producer=Producer('producer',queue)
consumer=Consumer('consumer',queue)
producer.start()
consumer.start()
producer.join()
consumer.join()
print("主线程结束")
3.5GIL
全局解释器锁(global interpreter lock),某个线程想要执行,必须先拿到 GIL,我们可以把 GIL 看作是“通行证”,并且在一个 Python 进程中,GIL 只有一个。拿不到通行证的线程,就不允许进入 CPU 执行。Python 中,无论是单核还是多核,同时只能由一个线程在执行。其根源是 GIL 的存在。
每次释放 GIL锁,线程进行锁竞争、切换线程,会消耗资源。这就导致打印线程执行时长,会发现耗时更长的原因。
并且由于 GIL 锁存在,Python 里一个进程永远只能同时执行一个线程(拿到 GIL 的线程才能执行),这就是为什么在多核CPU上,Python 的多线程效率并不高的根本原因