1、进程和线程
1.1、系统多任务机制
多任务操作机制的引入主要是在相同的硬件资源下怎么提高任务处理效率的!多任务的处理机制可以在提升任务处理效率的基础上,快速提升用户体验!
python本身也支持多任务处理,并提供了如下的操作方式
- 多线程多任务处理机制【小型民营企业】
- 多进程多任务处理机制【大型国企】
- 协成多任务处理机制
1.2进程、线程、协程
1.2.1、进程(Process)
进程:计算机中一个程序在一个数据集上一次动态执行的过程,主要包含三部分内容
- 程序:描述进程的功能以及处理流程
- 数据集:功能处理过程中需要的资源数据
- 进程控制:严格控制进程执行过程中的各种状态
通俗的讲:一个进程就是计算机上正在运行的一个程序
一个软件程序要运行,需要将软件依赖的数据加载到内存中,通过CPU进行运算并按照进程定义的逻辑结构进行进程控制,直到数据处理完成程序退出!
在程序实际的执行过程中,进程只是分配需要的数据资源,是程序的主体,实际上,是线程在运行程序,每个进程至少有一个线程(主线程)
1.2.2、线程(Thread)
计算机中程序运行的实际执行者就是线程,线程又轻量级进程,是一个CPU的执行单元,每个进程至少会有一个线程(主线程)用于执行程序
线程和进程对比
- 一个进程可以有多个线程,但是至少有一个主线程
- 一个线程只能属于一个进程
- 一个进程可以有多个线程,可以共享进程中的数据
- 线程是最小的运算单元,进程是最小的资源管理单元
1.2.3、串行、并行、并发
串行:就是传统意义上的同步、顺序的意思,按照一定的执行步骤顺序执行尅个环节
并行:传统意义上的异步、同时的意思,同时执行接受到的多个任务
并发:同时接收到多个任务,同时执行多个任务,但具体到某个时刻-只是在执行一个任务,只是在很短的时间内在多个任务间切换,模拟形成了多个任务同时执行的现象
大部分计算机中的应用程序的执行,一般都是并发执行机制的多任务处理机制
PYTHON为了保证多任务机制小的共享数据安全性和完整性,CPython官方解释器内置了一个GIL(Global Interceptor Lock:全局解释器锁),只允许在同一时间内CPU只能执行一个线程,所以在PYTHON的官方解释器下,所谓的多线程并发机制并不是多线程并行机制!
2、多线程编程
PYTHON本身对于多线程并发机制的支持是比较完善的
(1)在PYTHON2中提供了标准模块thread和threading支持多线程的并发编程,但由于过于底层,不适合新手操作
(2)PYTHON3中将thread模块进行了规范内置,更名为_thread,如果你不是并发编程的骨灰级爱好者,还是推荐使用更加简洁的threading模块进行并发编程
_thread模块并发任务的简单实现如下:
import _thread,time
def sing():
for i in range(10):
print("唱歌========")
def dance():
for i in range(10):
print("跳舞&&&&&&&&")
_thread.start_new_thread(sing,())
_thread.start_new_thread(dance,())
#def start_new_thread(function, args, kwargs=None):
#第一个参数是线程调用的方法,第二个参数是调用方法是需要的关键字参数,第三个也是调用方法是需要的可变关键字参数默认为None
time.sleep(1)
2.1、PYTHON中的多线程
官方推荐的 threading 模块的多线程并发编程机制,结合时下流行的面向过程/面向对象的
编程处理模式,主要有两种操作方式
- 函数式的线程创建方式,适合面向过程程序的并发编程实现
- 面向对象的创建方式,适合面向对象程序的并发编程实现
2.1.1、threading模块的属性的方法
名称 | 描述 |
---|---|
Thread | 线程类,用于创建和管理线程 |
Event | 事件类,用户线程同步 |
Condition | 条件类,用于线程同步 |
Lock/RLock | 锁类,用于线程同步 |
Barrir | 线程同步类型 |
local | 线程局部数据类 |
Semaphore/BoundedSemaphore | 信号量类,用于线程同步 |
active_count()/activeCount() | 获取当前存活的所有线程的数量 |
current_thread()/currentThread() | 获取正在执行的线程对象 |
get_ident() | 获取运行中的程序当前的唯一编号 |
enumerate() | 获取所有存活状态的线程列表 |
stack_size([size]) | 获取线程占用内存栈的大小 |
main_therad() | 获取主线程 |
2.1.2、Thread类型属性和方法
名称 | 描述 |
---|---|
init(group,target,name,args,kwargs) | 构造方法,创建线程的类型,group:线程组,target:调用的方法,args,kwagrs:面向过程时,方法的两种参数 |
is_alive()/isAlive() | 判断当前线程是否存活 |
run() | 线程执行方法,自定义线程必须重写该函数 |
start() | 线程启动方法 |
jion([timeout=None]) | 线程独占,等待当前线程运行结束或超时 |
ident | 标识当前线程的唯一编号 |
name | 当前线程的名称 |
daemon | 布尔值,判断当前线程是否守护线程,True时,主线程结束,线程也结束 |
3.1、函数式编程:火车站售票
import threading,time
#定义票的总数
chepiao = 20
#定义线程锁对象【互斥锁|可重用锁】
lock = threading.Lock()
def shoupiao():
global chepiao
while True:
time.sleep(0.5)
#线程锁:上锁
if lock.acquire():
if chepiao > 0:
print("线程:" ,threading.currentThread().getName(),"剩余车票:"+ str(chepiao))
chepiao -= 1
else:
print("票售完了")
lock.release()
break
#线程锁:释放锁、解锁
lock.release()
if __name__ == '__main__':
#单线程
# t1 = threading.Thread(target=shoupiao())
# t1.start()
#多线程
for i in range(5):
t = threading.Thread(name ="窗口" + str(i),target=shoupiao)
t.start()
#多线程更加快速的运行完
3.2、线程通信——事件对象
import threading,time
'''
小贩:生产油条
顾客:消费油条
需求:顾客去消费
'''
event = threading.Event()
def xiao_fan():
print("XF:炸油条")
time.sleep(1)
#添加标记:告诉其他线程可以执行
event.set()
#清除标记
event.clear()
print("XF:卖油条")
#线程等待
event.wait()
print("XF:谢谢光临")
def gu_ke():
#线程等待,等待被标记
event.wait()
print("GK:买油条")
print("GK:吃油条")
time.sleep(1)
event.set()
event.clear()
print("GK:油条真好吃")
if __name__ == '__main__':
xf = threading.Thread(name='小贩:',target=xiao_fan)
gk = threading.Thread(name='顾客:',target=gu_ke)
xf.start()
gk.start()
3.3、线程通信-条件对象
'''
生产者与消费者问题
公共的数据:【篮子】
生产者:蒸包子——放到篮子里,并告诉【唤醒】所有消费者可以吃了
如果篮子满了,所有生产者 等待
消费者:吃包子----从篮子拿包子,并告诉所有生产者可以生产了
如果篮子空了,所有消费者 等待
'''
import threading,time,random
#创建条件对象
con = threading.Condition()
'''
acquire()#上锁
release()#解锁
wait()#等待
wait_for()#等待
notify()#唤醒
notify_all()#唤醒所有
'''
basket = list()
def product():
while True:
time.sleep(4)
#上锁
con.acquire()
if len(basket) > 40:
print(threading.currentThread().getName(),"篮子满了,停止生产!")
con.wait()
else:
#生产一个包子
_no = random.randint(0,10)
print(threading.currentThread().getName(),"蒸了"+str(_no)+"个包子。消费者可以吃了")
basket.append(_no)
#唤醒所有消费者
con.notify()
#解锁
con.release()
def consumer():
while True:
time.sleep(4)
#上锁
con.acquire()
print(len(basket))
if len(basket) <= 0:
print(threading.currentThread().getName(),"包子没有了,赶快生产!")
#消费者等待
con.wait()
else:
_no = basket.pop()
print(threading.currentThread().getName(),"消费了"+str(_no)+"个包子")
#通知所有生产者生产
con.notify()
#解锁
con.release()
if __name__ == '__main__':
for i in range(2):
pro = threading.Thread(name="生产者"+str(i),target=product)
pro.start()
for j in range(2):
consu = threading.Thread(name="消费者"+str(j),target=consumer)
consu.start()
3.4、线程通信_队列(Queue)
queue特点:
(1)先进先出,一段进,一段出
(2)线程安全,在同一个时刻,只能有一个线程访问
# 生产者消费者
import threading, queue, time, random
# 创建一个队列,存储数据
basket = queue.Queue(10)
'''
put(data [, timeout=None])
向队列中添加数据,如果队列已满~一直等待[到超时]
get([timeout=None])
从队列中获取数据,如果队列已空~一直等待[到超时]
特性:线程安全!
'''
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(1)
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()
3.5、线程_面向对象编程
import threading,random
class Mytread(threading.Thread):
def __init__(self,name):
super().__init__(name = name)
self.con = threading.Condition()
self.song = ['那一夜','走马','明天你好',"会呼吸的痛"]
def run(self):
print(threading.currentThread().getName(),"正在执行")
self.sing(random.choice(self.song))
def sing(self,song):
self.con.acquire()
print("唱了一首",song)
self.song.remove(song)
self.con.release()
#创建三个线程对象
m1 = Mytread("线程1")
m2 = Mytread("线程2")
m3 = Mytread("线程3")
m1.start()
m2.start()
m3.start()