进程、线程、协程对比
进程是操作系统资源分配的单位
线程是CPU调度的单位
进程切换需要的资源最大,效率很低
线程切换需要的资源一般,效率一般(当然在不考虑GIL的情况下)
协程切换任务资源小,效率高
多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中,所以是并发
同步和异步
异步:调用在发出之后,这个调用就直接返回,不管有无结果:异步是过程
非阻塞:关注的是程序在等待调用结果(消息,返回值)时的状态,指在不能立刻得到结果之前,该调用不会阻塞当前线程
同步异步的区别
同步:一个服务的完成需要依赖其他服务时,只有等待被依赖的服务完成后,才算完成,这是一种可靠的服务序列。要么成功都成功,要么失败都失败,
服务的状态可以保持一致
异步:一个服务的完成需要依赖其他服务时,只通知其他依赖服务开始执行,而不需要等待被依赖的服务完成,此时该服务就算完成了。被依赖的服务是
否最终完成无法确定,因此他是一个不可靠的服务序列
消息通知中的同步和异步:
同步:当一个同步调用发出后,调用者一直等待返回消息(或者调用结果)通知后,才能进行后续的执行
异步:当一个异步过程调用发出后,调用者不能立刻得到返回消息(结果)。在调用结束之后,通过消息回调来通知调用者是否调用成功。
阻塞和非阻塞区别:
阻塞:阻塞调用是指调用结果返回之前,当前线程会被挂起,一直处于等待消息通知,不能够执行其他业务,函数只有在得到结果之后,才会返回
非阻塞:非阻塞和阻塞的结果相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回
同步与异步是对应的,他们是线程之间的关系,两个线程之间要么是同步的,要么是异步的
阻塞与非阻塞是同一个线程来说的,在某个时刻,线程要么处于阻塞,要么处于非阻塞。
阻塞是使用同步机制的结果,非阻塞则是使用异步机制的结果
守护线程
setDaemon() 将当前线程设置成守护线程来守护主线程:-当主线程结束后,守护线程也就结束,不管是否执行完成
-应用场景:qq多个聊天窗口,就是守护线程
注意:需要在子线程开启的时候设置成守护线程,否则无效
import time,threading
def dance():
for i in range(3):
time.sleep(0.5)
print("正在跳舞")
def sing():
for i in range(3):
time.sleep(0.8)
print('正在唱歌')
t1 = threading.Thread(target=dance)
t2 = threading.Thread(target=sing)
t1.setDaemon(True)
t2.setDaemon(True)
t1.start()
t2.start()
time.sleep(1)
print("时间结束。ending")
# 正在跳舞
# 正在唱歌
# 时间结束。ending
# ------run------- 0
# ------run------- 1
# ------run------- 2
# 正在跳舞
-getName() 获取线程的名称
-setName() 设置线程的名称
-isAlive() 判断当前线程存活状态
import time,threading
def dance():
for i in range(3):
time.sleep(0.8)
print('正在跳舞')
def sing():
for i in range(3):
time.sleep(0.8)
print("正在唱歌")
t1 = threading.Thread(target=dance)
t2 = threading.Thread(target=sing)
t1.setName('线程1')
# t2.setName('线程2')
print(t1.is_alive())
t1.start()
print(t1.is_alive())
t2.start()
print(t1.getName())
print(t2.getName())
# False
# True
# 线程1
# Thread-2
# ------run------- 0
# ------run------- 1
# ------run------- 2
# 正在跳舞
# 正在唱歌
# 正在唱歌
# 正在跳舞
# 正在唱歌
# 正在跳舞
threading.currentThread():返回当前的线程变量
threading.enumrate(): 返回一个包含正在运行的线程list,正在运行线程启动后、结束前,不包括启动前和终止后的线程。
threading.activeCount():返回正在运行的线程数量,与len(threading.enumrate())有相同的结果
import threading,time
def dance():
print('dance',threading.current_thread())
for i in range(3):
time.sleep(0.5)
print('正在跳舞')
def sing():
print('sing',threading.current_thread())
for i in range(3):
time.sleep(0.5)
print('正在唱歌')
t1 = threading.Thread(target=dance)
t2 = threading.Thread(target=sing)
print(threading.current_thread())
# print(threading.enumerate())
t1.start()
t2.start()
print('启动后的线程列表-->',threading.enumerate())
print(threading.active_count())
print(len(threading.enumerate()))
# <_MainThread(MainThread, started 1536)>
# dance <Thread(Thread-1, started 2000)>
# sing <Thread(Thread-2, started 10484)>
# 启动后的线程列表--> [<_MainThread(MainThread, started 1536)>, <Thread(Thread-1, started 2000)>, <Thread(Thread-2, started 10484)>]
# 3
# 3
# 正在唱歌
# 正在跳舞
# 正在唱歌
# 正在跳舞
# 正在唱歌
# 正在跳舞
from threading import Thread
class MyThread(Thread):
def __init__(self,num):
super(MyThread,self).__init__()
self.num =num
def run(self):
for i in range(self.num):
print('------run-------',i)
if __name__ == '__main__':
my = MyThread(3)
my.start()
# ------run------- 0
# ------run------- 1
# ------run------- 2
多线程开发的时候共享全局变量会带来资源竞争
# 多线程开发的时候共享全局变量会带来资源竞争的效果,也就是数据不安全
import threading,time
g_num = 0
def func1(n):
global g_num
for i in range(n):
time.sleep(0.01)
g_num+=1
print('--func1---',g_num)
def func2(n):
global g_num
for i in range(n):
time.sleep(0.01)
g_num+=1
print('--func2--',g_num)
if __name__ == '__main__':
t1 = threading.Thread(target=func1,args=(10000,))
t2 = threading.Thread(target=func2,args=(10000,))
t1.start()
线程锁
from threading import Thread,Lock
# 互斥锁
lock = Lock() # 创建锁
lock.acquire() # 加锁
lock.release() # 释放锁
g_num = 0
def func1(n):
global g_num
for i in range(n):
lock.acquire()
g_num+=1
lock.release()
print('--in func1--',g_num)
def func2(n):
global g_num
for i in range(n):
lock.acquire()
g_num+=1
lock.release()
print('--in func2--',g_num)
if __name__ =='__main__':
lock = Lock()
t1 = Thread(target=func1,args=(10000,))
t2 = Thread(target=func2,args=(10000,))
t1.start()
t2.start()
# --in func1-- 10000
# --in func2-- 20000
# 在多个线程共享资源的时候,如果两个线程分别占有一部分资源,并且同时等待对方的资源,就会造成死锁现象
# 如果锁之间相互嵌套,就有可能出现死锁。因此尽量不要出现锁之间的嵌套
import threading,time
printer_lock = threading.Lock() # 创建打印机锁
paper_lock =threading.Lock() #简历锁
class Printer(threading.Thread):
def run(self):
print('--printer start--')
paper_lock.acquire()
time.sleep(1)
print("编写简历1")
printer_lock.acquire()
print("正在使用打印机1")
printer_lock.release()
paper_lock.release()
print("printer end")
class Paper(threading.Thread):
def run(self):
print("---paper start--")
printer_lock.acquire()
time.sleep(1)
print('编写简历2')
paper_lock.acquire()
print("正在使用打印机2")
printer_lock.release()
paper_lock.release()
print('paper end')
if __name__ == '__main__':
printer = Printer()
paper = Paper()
printer.start()
paper.start()
# --printer start--
# ---paper start--
# 编写简历2
# 编写简历1
# 一个threadLocal 变量虽然是全局变量,但是每个线程都只能读写自己线程的独立副本,互不干扰。
# threadLocal 解决了参数在一个线程中各个函数之间相互传递的问题
import threading
local = threading.local()
def func1():
print(local.num)
def func2():
local.num = 5
local.num+=10
print(threading.current_thread())
func1()
t1 = threading.Thread(target=func2)
t1.start()
# <Thread(Thread-1, started 3284)>
# 15
线程和进程的对比
from multiprocessing import Process
import time
def func1():
start = time.time()
i = 0
for i in range(20000000):
i +=1
end = time.time()
print("func1--total_time:",end-start)
def func2():
start = time.time()
i = 0
for i in range(20000000):
i += 1
end = time.time()
print("func2--total_time:", end - start)
if __name__ == '__main__':
p1 = Process(target=func1)
p2 = Process(target=func2)
p1.start()
p2.start()
# func2--total_time: 1.0018956661224365
# func1--total_time: 1.0268356800079346
import threading
import time
def func1():
start = time.time()
i = 0
for i in range(20000000):
i +=1
end = time.time()
print("func1--total_time:",end-start)
def func2():
start = time.time()
i = 0
for i in range(20000000):
i += 1
end = time.time()
print("func2--total_time:", end - start)
if __name__ == '__main__':
t1 = threading.Thread(target=func1)
t2 = threading.Thread(target=func2)
t1.start()
t2.start()
# func1 - -total_time: 1.7554385662078857
# func2 - -total_time: 1.8083033561706543
import time
num =0
start =time.time()
for i in range(20000000):
i+=1
end = time.time()
print(end-start) #3.700019598007202
协程
# 协程切换一百万次
import time
def func1():
start = time.time()
for i in range(1000000):
yield
end = time.time()
print("in func1 total_time:",end-start)
def func2(g):
start = time.time()
for i in range(1000000):
g.__next__()
end = time.time()
print('in func2 total_time:',end-start)
g = func1()
func2(g)
# in func2 total_time: 0.11689877510070801
生产者模式和消费者模式
q = queue.Queue(3) 3表示只能存放3个数据
参数:maxsize 是队列中允许的最大项数。如果省略此参数,则无大小限制。返回值q是队列对象
2.put()方法,向队列中存放数据。如果队列已满,此方法阻塞直至有空间可用为止。
3.get()返回q中的一个项目。如果q为空,此方法将阻塞,直到队列中有项目可用为止
4.get_nowait():不等待,直到抛出异常
5.full()如果q已满,返回为True
6.q.empty()如果调用此方法时q为空,返回为True
7.qsize()获取队列中数据的个数
import queue
q = queue.Queue(2)
q.put("hello")
q.put("world")
print(q.full())
print(q.qsize())
print(q.get())
print(q.get())
# True
# 2
# hello
# world
import queue
import threading
import time
list = []
def produce(name):
for i in range(1,11):
print("%s生产了包子%d"%(name,i))
q.put(i)
time.sleep(0.5)
def custom(name):
while True:
print("%s吃了包子%d"%(name,q.get(timeout=10)))
if __name__ =='__main__':
q = queue.Queue()
t1 = threading.Thread(target=produce,args=('johnson',))
t2 = threading.Thread(target=custom,args=('may',))
t1.start()
t2.start()
# johnson生产了包子1
# may吃了包子1
# johnson生产了包子2
# may吃了包子2
# johnson生产了包子3
# may吃了包子3
# johnson生产了包子4
# may吃了包子4
# johnson生产了包子5
# may吃了包子5
# johnson生产了包子6
# may吃了包子6
# johnson生产了包子7
# may吃了包子7
# johnson生产了包子8
# may吃了包子8
# johnson生产了包子9
# may吃了包子9
# johnson生产了包子10
# may吃了包子10
练习
"""
创建两个进程:进程A和进程B,用jcq消息队列实现他们的通信。
1、进程A里创建两个线程:线程A1 、线程A2.
在进程A1里创建一个a列表 [1,2,3,4,5,6], 线程A1用消息队列将列表传递给线程A2,线程A2.
对列表进行筛选,删除掉奇数。然后用进程jcq消息队列发送给进程B
2、在进程B里创建两个线程:线程B1、线程B2
线程B2.接受进程A发过来的数据,并把这些数据,原封不动的用线程消息队列传递给线程B1
线程B1求出所有数的和并打印结果
"""
from multiprocessing import Process,Queue
from threading import Thread
import queue
def JcA(Q):
qa = queue.Queue()
a=[1, 2, 3, 4, 5, 6]
xca1 = Thread(target=XcA1,args=(qa,a))
xca2 = Thread(target=XcA2,args=(qa,Q))
xca1.start()
xca1.join()
xca2.start()
xca2.join()
def XcA1(qa,lst):
for i in lst:
qa.put(i)
def XcA2(qa,Q):
for i in range(qa.qsize()):
num = qa.get()
if num%2==0:
pass #放到进程消息队列中
Q.put(num)
# 2、在进程B里创建两个线程:线程B1、线程B2
# 线程B2.接受进程A发过来的数据,并把这些数据,原封不动的用线程消息队列传递给线程B1
# 线程B1求出所有数的和并打印结果
def JcB(Q):
qb = queue.Queue()
xcb1 = Thread(target=XcB1, args=(qb,))
xcb2 = Thread(target=XcB2, args=(Q,qb))
xcb2.start()
xcb2.join()
xcb1.start()
xcb1.join()
def XcB1(qb):
# pass #从线程消息队列中取数据,求出所有数的和并打印结果
sum = 0
for i in range(qb.qsize()):
sum+=qb.get()
print(sum)
def XcB2(Q,qb):
# pass#从进程队列中取数据,用线程消息队列传递给B1
for i in range(Q.qsize()):
qb.put(Q.get())
if __name__ == '__main__':
Q = Queue()
pa = Process(target=JcA,args=(Q,))
pb = Process(target=JcB,args=(Q,))
pa.start()
pa.join()
pb.start()