多任务操作机制的引入:在相同的硬件资源下提高任务的处理效率
在提升任务处理效率的基础上,提升用户体验
Python本身多任务处理机制
多线程任务处理机制(小民营企业式)
多进程任务处理机制(大型国企式)
协程多任务处理机制(家庭作坊式)
进程:计算机中一个程序在一个数据集上一次动态执行的过程
程序:描述进程的功能以及处理流程
数据集:功能处理过程中需要的资源数据
进程控制:严格控制进程执行过程中的各种状态(堵塞...等)
一个软件程序要运行,需要将软件依赖的数据加载到内存之中,通过CPU进行运算,并按照程序定义的逻辑进行流程控制,直到数据处理完成之后程序退出
在程序执行的过程中,进程只是负责分配需要的资源数据,是程序的主体
程序运行过程中真正运行的是线程
每个进程中至少有一个线程(主线程)
进程主要是用来分配任务,真正干活的是线程
线程:计算机中程序运行的实际执行者,又称轻量级线程,是一个CPU的执行单元。
每个进程中至少有一个线程(主线程)
一个线程只能属于一个进程
一个进程可以有多个线程,多个线程之间可以共享进程中提供的数据
CPU运算分配给线程,CPU 上运算执行的就是线程
线程是最小的运行单元, 进程是最小的资源管理单元
形象化:
进程:公司老板,提供资源给线程使用
主线程:管理人员/干活
子线程:干活的人
串行:传统意义上的同步,顺序的意思,按照一定的执行步骤顺序执行每个环节
并行:传统意义上的异步,同时的意思,多个任务同时执行,同时执行接收到的多个任务
并发:同时接收到多个任务,同时执行多个任务,但是具体到某个时刻的时候只在执行其中 一个任务,只不过是在很短的时间内在多个任务之间切换,模拟形成了多个任务在同 时执行的现象
Python中支持并行操作,但为了保证多任务机制下的共享数据的安全,python内置了一个GIL(全局解释器)只允许同一时间内CPU只能执行一个线程。所以在python的官方解释器下,多线程是多线程并发机制
Python中多线程并发机制
提供了两种模块:_thread 和 threading
_thread 适合用于大神
threading 适合新手使用(官方推荐)
_thread 的使用:
#引入需要的模块
import _thread, time
#定义第一个函数
def sing():
for i in range(20):
print("篝火盛宴!!!")
#定义第二个函数
def info():
for i in range(20):
print("载歌载舞!!!")
#创建多个线程
_thread.start_new_thread(sing, ())
_thread.start_new_thread(info, ())
#让系统主线程等待子线程运行结束
time.sleep(3)
threading 的使用:
可以实现面向过程的并发编程
也可以实现面向对象的并发编程
常用的threading 模块属性和方法
Thread 线程类,用于创建和管理线程
Event 事件类,用于作线程同步 添加一个标志,通过标志进行等待,运 行,清除
Condition 条件类,用于作线程同步 可以实现加锁,开锁,等待,运行等
Lock/RLock 锁类,用于线程同步 加锁/开锁
Timer 延时线程,用于在一定事件后在执行一个异步函数
常见的Thread 模块属性和方法
init(name线程名称,target面向过程开发时指定执行的函数,args面向过程开发时指定给函数的参数) 构建方法,构建线程类型
run() 面向过程开发时,需要重写的方法,线程一旦启动,会自动执行
start() 线程启动的方法,会创建一个新的线程,并自动调用run方法
join 独占模式
deamon 守护线程
基于threading模块的多线程并发编程(火车票售票窗口)
#引入需要的模块
import threading, time
#定义车票
che_piao = 10
#定义线程锁对象
lock = threading.Lock()
#定义售票函数
def shou_piao():
global che_piao
while True:
time.sleep(0.5)
#线程锁上锁
if lock.acquire():
if che_piao > 0:
che_piao -= 1
print("售出一张车票")
else:
print("本车次票已售完")
# 线程锁解锁
lock.release()
break
lock.release()
if __name__ == "__main__":
# #单线程售票系统
# shou_piao()
#多线程售票系统
for i in range(5):
t = threading.Thread(name = "窗口"+str(i), target=shou_piao)
t.start()
# #定义一个售票窗口
# t1 = threading.Thread(name='一号售票窗口', target=shou_piao)
#
# #启动代码
# t1.start()
线程管理——锁(Lock/RLock)
多线程程序在运行过程中,由于多个线程访问的时同一部分数据,很容易会造成共享数据访问冲突的现象,如果一旦出现冲突程序就会出现与执行结果不符合期望的结果
python 提供了两种线程锁的操作
同步锁/互斥锁: Lock
可重用锁: RLock
锁的操作
acquire(), 获取锁,上锁
Release(), 释放锁,解锁
#引入需要的模块
import threading, time
#定义车票
che_piao = 10
#定义线程锁对象
lock = threading.Lock()
#定义售票函数
def shou_piao():
global che_piao
while True:
time.sleep(0.5)
#线程锁上锁
if lock.acquire():
if che_piao > 0:
che_piao -= 1
print("售出一张车票")
else:
print("本车次票已售完")
# 线程锁解锁
lock.release()
break
lock.release()
if __name__ == "__main__":
# #单线程售票系统
# shou_piao()
#多线程售票系统
for i in range(5):
t = threading.Thread(name = "窗口"+str(i), target=shou_piao)
t.start()
# #定义一个售票窗口
# t1 = threading.Thread(name='一号售票窗口', target=shou_piao)
#
# #启动代码
# t1.start()
线程管理——死锁(哲学家吃饭问题)
线程锁固然功能强大,可以管理多个线程之间的共享数据问题 但是同时它的强大也带来了比较纠结的问题,需要开发人员对于锁定的数据有一个良好的认知,否则特别容易造成死锁的现象,比较著名的哲学家吃饭问题就是死锁的典型代表
由于计算机运算速度较快,所以有两种方案可以将问题放大
给执行函数添加休眠时间
添加线程数量
死锁并不是每次都会出现的,而是程序在执行过程中,根据系统 CPU 时间片的切换机制恰 好遇到了重复上锁的情况,就会死锁
死锁情况可以用重用锁 RLock 进行锁定处理
#引入需要的模块
import threading
#定义两把锁
lock_a = threading.Lock()
lock_b = threading.Lock()
def ZhexueA():
for i in range(200):
if lock_a.acquire():
if lock_b.acquire():
print("你给我刀子,我给你叉子")
lock_b.release()
lock_a.release()
def ZhexueB():
for j in range(200):
if lock_b.acquire():
if lock_a.acquire():
print("你给我叉子, 我给你刀子")
lock_a.release()
lock_b.release()
if __name__ == "__main__":
bj = threading.Thread(target=ZhexueA)
bj.start()
ss = threading.Thread(target=ZhexueB)
ss.start()
线程管理——事件
针对多个线程之间的通信的问题,python提供了Event进行处理
Event.set() 添加一个标记状态
Event.clear() 清除标记状态
Event.wait() 事件对象操作的当前线程等待,直到该对象被标记状态
Event实现
"""
小贩:生产油条
顾客:消费油条
需求:顾客消费
两个线程之间的通信:事件对象, threading.Event
event.set() 添加标记
event.wait() 线程等待
event.clear()
"""
#引入需要的模块
import threading,time
#定义一个事件对象
event = threading.Event()
#定义商贩
def shang_fan():
print("商贩:铁锅油条")
time.sleep(2)
#添加标记
event.set()
#清除标记
event.clear()
print("商贩:刚出锅的")
#线程等待
event.wait()
print("商贩:谢谢光临")
#定义顾客
def gu_ke():
#线程等待
event.wait()
print("顾客:老板,两根油条")
time.sleep(2)
print("顾客:外焦里嫩")
print("顾客:下次还来")
#添加标记
event.set()
if __name__ == "__main__":
sf = threading.Thread(target=shang_fan)
gk = threading.Thread(target=gu_ke)
sf.start()
gk.start()
线程管理——条件
线程条件 Condition 对象,也是多线程并发模式下一种线程之间的通信
Condition.acquire() 锁定
Condition.release() 解锁
Condition.wait() 释放锁
Condition.notify() 唤醒
Condition 的实现
#引入需要的对象
import threading, time, random
#创建一个条件对象
con = threading.Condition()
#创建篮子
basket = list()
#生产者
def product():
while True:
time.sleep(1)
#上锁
con.acquire()
if len(basket) > 20:
print(threading.current_thread().getName(), ":篮子满了,快来吃包子吧")
con.wait()
else:
#生产一个包子
_no = random.randint(1,10)
print(threading.current_thread().getName(), ":蒸好了一个包子", _no)
basket.append(_no)
con.notify()
#解锁
con.release()
def consumer():
while True:
time.sleep(0.5)
#上锁
con.acquire()
if len(basket) <= 0:
print(threading.current_thread().getName(), ":吃光了,快点去蒸包子吧")
con.wait()
else:
_no = basket.pop()
print(threading.current_thread().getName(), ":吃了一个包子", _no)
con.notify()
#解锁
con.release()
if __name__ == "__main__":
#生产厂家
for i in range(5):
p = threading.Thread(name="生产者" + str(i) + "号", target=product)
p.start()
#消费人群
for j in range(5):
c = threading.Thread(name="消费者" + str(j) + "号", target=consumer)
c.start()
线程管理——队列
多线程并发编程的重点,是线程之间共享数据的访问问题和线程之间的通信问题 为了解决线程之间数据共享问题,PYTHON 提供了一个数据类型【队列】可以用于在多线程 并发模式下,安全的访问数据而不会造成数据共享冲突
put([timeout=None]) 向队列中添加数据,队列如果满了,一直阻塞直到超时或者队 列中有数据被删除之后添加成功
get([timeout=None]) 从队列中获取数据,如果队列为空,一直阻塞直到超时或者队 列中添加数据之后获取成功
队列实现Queue:
#引入需要的模块
import threading, queue, time, random
# #创建一个条件对象
# con = threading.Condition()
#定义一个队列储存数据
basket = queue.Queue(20)
def product():
while True:
time.sleep(1)
_no = random.randint(0, 10)
try:
basket.put(_no, timeout=1)
print("生产者生产了一个数据", _no)
except:
print("篮子满了")
def consumer():
while True:
time.sleep(0.5)
try:
_no = basket.get(timeout = 1)
print("消费者取走了一个数据", _no)
except:
print("篮子空了.................")
if __name__ == "__main__":
for i in range(2):
p = threading.Thread(target=product)
p.start()
for j in range(1):
c = threading.Thread(target=consumer)
c.start()