同步和异步、阻塞和非阻塞
- 异步: 多任务, 多个任务之间执行没有先后顺序,可以同时运行,执行的先后顺序不会有什么影响,存在的多条运行主线
- 同步: 多任务, 多个任务之间执行的时候要求有先后顺序,必须一个先执行完成之后,另一个才能继续执行, 只有一个主线
- 阻塞:从调用者的角度出发,如果在调用的时候,被卡住,不能再继续向下运行,需要等待,就说是阻塞
- 非阻塞: 从调用者的角度出发, 如果在调用的时候,没有被卡住,能够继续向下运行,无需等待,就说是非阻塞
1)同步、异步
函数或方法被调用的时候,调用者是否得到最终的结果。
- 直接得到最终结果的结果,就是同步调用。(打饭模型,打饭不打好不走开,直到打饭给我后才离开)
- 不直接得到的最终的结果,就是异步调用。(打饭,不会一直等着,会时不时的过来看看,打完了把饭拿走,异步不保证多长时间打完了饭)
2)阻塞、非阻塞:
函数或方法调用的时候,是否立即返回。
- 立即返回就是非阻塞调用。
- 不立即返回就是阻塞调用。
3)区别:
- 同步、异步,与阻塞、非阻塞不相关。
- 同步、异步强调的是结果。阻塞和非阻塞强调的是时间,是否等待。
- 同步与异步区别在于:调用者是否得到了想要的最终结果。
- 同步就是一直要执行到返回最终结果。异步就是直接返回了,但是返回的不是最终的结果,调用者不能通过这种调用得到结果,还要通过被调用者,使用其他方式通知调用者,来取回最终结果。
- 阻塞与非阻塞的区别在于,调用者是否还能干其他的事情。
- 阻塞,调用者只能干等。非阻塞,调用者可以先忙一会别的,不用一直等。
4)联系:
- 同步阻塞:调用者阻塞,直到等到拿到最终结果。(打饭模型,什么事情也不敢,就等着打饭,打饭是结果,什么也不干,一直在等着,同步加阻塞)
- 同步非阻塞:(等着打饭,但是可以玩会手机,看看电视,打饭是结果,但是不用一直在等着)
- 异步阻塞:(我要打饭,你说等着较好,并没有返回饭给我,我啥事不干,就干等着饭好了叫我)
- 异步非阻塞:回调的话。(我要打饭,你说等较好,并没有返回饭给我,可以在旁边看看电视,玩玩手机,饭好了叫我)
进程、线程和协程之间的区别和联系
定义
- 进程: 一个运行起来的程序或者软件叫做进程,进程是操作系统分配资源的 基本单位
- 线程:指进程内的一个执行单元,也是进程内的可调度实体。
- 协程:是一种程序组件,是由子例程(过程、函数、例程、方法、子程序)的概念泛化而来的,子例程只有一个入口点且只返回一次,而协程允许多个入口点,可以在指定位置挂起和恢复执行。
区别
- 进程是资源分配的最小单位,线程是CPU调度的最小单位.
- 进程是操作系统资源分配的单位
- 线程是CPU调度的单位
- 进程切换需要的资源最大,效率很低
- 线程切换需要的资源一般,效率一般
- 协程切换需要资源很小,效率高
线程与进程的区别
- 同一个进程中的线程共享同一内存空间,但是进程之间是独立的。
- 同一个进程中的所有线程的数据是共享的(进程通讯),进程之间的数据是独立的。
- 对主线程的修改可能会影响其他线程的行为,但是父进程的修改(除了删除以外)不会影响其他子进程。
- 一个进程至少有一个线程
- 同一个进程的线程之间可以直接通信,但是进程之间的交流需要借助中间代理(队列)来实现
- 创建新的线程很容易,但是创建新的进程需要对父进程做一次复制。
- 一个线程可以操作同一进程的其他线程,但是进程只能操作其子进程。
- 线程启动速度快,进程启动速度慢(但是两者运行速度没有可比性)。
协程与线程的比较
- 一个线程可以多个协程,一个进程也可以单独拥有多个协程,这样python中则能使用多核CPU。
- 线程进程都是同步机制,而协程则是异步
- 协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态
函数
# 线程
from threading import Thread
# 线程间队列
import queue
# 进程、进程间队列
from multiprocessing import Process, Queue
#时间模块
import time
#注:线程队列与进程队列不通用,可以通过 as 创建 昵称
#进程间队列:from multiprocessing import Queue as PQueue
#线程间队列:from queue import Queue as QQueue
# 进程一的线程一
# 将数据放入线程间的队列
def process1_thread1(q1):
l = [1, 2, 3]
for i in l:
q1.put(i)
print("进程一的线程一放第{}数据....".format(i))
# 进程一的线程二
# 从线程中取出放入进程间的队列
def process1_thread2(q1, q2):
while True:
try:
data = q1.get(timeout=2)
print("进程一的线程二取数据:",data)
q2.put(data)
print("进程一的线程二放数据....")
except:
break
# 进程二的线程一
# 放入线程间的队列
def process2_thread1(q2, q3):
while True:
try:
data = q2.get(timeout=2)
print("进程二的线程一取数据:",data)
q3.put(data)
print("进程二的线程一放数据....")
except:
break
# 进程二的线程二
# 从线程间的队列取出并输出
def process2_thread2(q3):
while True:
try:
data = q3.get(timeout=2)
print("进程二的线程二取数据....",data)
except:
break
def process1(q2):
# 创建线程间的队列
q1 = queue.Queue()
# 创建进程一的两个线程
p1t1 = Thread(target=process1_thread1, args=(q1,))
p1t2 = Thread(target=process1_thread2, args=(q1, q2))
# 启动两个线程
p1t1.start()
p1t2.start()
# 阻塞,防止死机
p1t1.join()
p1t2.join()
def process2(q2):
# 创建线程间通信
q3 = queue.Queue()
# 创建进程二的两个线程
p2t1 = Thread(target=process2_thread1, args=(q2, q3))
p2t2 = Thread(target=process2_thread2, args=(q3,))
# 启动两个线程
p2t1.start()
p2t2.start()
# 阻塞,防止死机
p2t1.join()
p2t2.join()
if __name__ == '__main__':
# 创建进程间队列
q2 = Queue()
#创建进程一
p1 = Process(target=process1, args=(q2,))
#启动进程一
p1.start()
#创建进程二
p2 = Process(target=process2, args=(q2,))
#启动进程二
p2.start()
函数
from threading import Thread
import requests
import time
import UserAgent
from queue import Queue
base_url = 'https://careers.tencent.com/tencentcareer/api/post/Query?timestamp=1562211235205&countryId=&cityId=&bgIds=&productId=&categoryId=&parentCategoryId=&attrId=&keyword=python&pageIndex={}&pageSize=10&language=zh-cn&area=cn'
headers = UserAgent.getUserAgentC()
def getUrl_and_putData(q,q2):
while True:
try:
url = q.get(timeout = 2)
response = requests.get(url,headers = headers).json()
data = response["Data"]["Posts"][0]["RecruitPostName"]
q2.put(data)
except:
break
def getData(q2):
while True:
try:
data = q2.get(timeout = 2)
print(data)
time.sleep(1)
except:
if flag:
print("没有数据了,退出...")
break
else:
print("队列中还没数据,等待ing...")
if __name__ == '__main__':
flag = False
q = Queue()
q2 = Queue()
for i in range(75):
url = base_url.format(i)
q.put(url)
for i in range(1,4):
t = Thread(target=getUrl_and_putData,args=(q,q2))
t.start()
t.join()
for i in range(1,4):
t = Thread(target=getData,args=(q2,))
t.start()
flag = True
类
#导入程序所用到的资源
from threading import Thread
from queue import Queue
import requests
#随机获取请求头
from fake_useragent import UserAgent
#定义起始URL
base_url = 'https://careers.tencent.com/tencentcareer/api/post/Query?timestamp=1562211235205&countryId=&cityId=&bgIds=&productId=&categoryId=&parentCategoryId=&attrId=&keyword=python&pageIndex={}&pageSize=10&language=zh-cn&area=cn'
headers = {"User-Agent": UserAgent().random}
#生产者
class Produce(Thread):
def __init__(self,name,url_q,data_q):
#继承父类初始化方法
super().__init__()
self.name =name
self.url_q = url_q
self.data_q = data_q
#重写run方法
def run(self):
while True:
try:
#从url队列中取出url
url = self.url_q.get(timeout=1)
print(self.name,url)
#获取页面资源并将其转换为json格式
response = requests.get(url,headers = headers).json()
#将数据放入data队列
self.data_q.put(response)
except:
break
#消费者
class Consumer(Thread):
def __init__(self,name,data_q):
#继承父类初始化方法
super().__init__()
self.name = name
self.data_q = data_q
#重写run方法
def run(self):
while True:
try:
#将数据从data队列中取出
data = self.data_q.get(timeout=1)
#输出json中所需要的数据
print(self.name,data["Data"]["Posts"][0]["RecruitPostName"])
except:
# 如果flag为true时退出循环
if flag:
break
#主函数
if __name__ == '__main__':
#创建队列
url_q = Queue()
data_q = Queue()
#定义一个退出标志
flag = False
#将拼接url
for i in range(1,75):
url = base_url.format(i)
#将url放入url队列中
url_q.put(url)
#定义消费者名称列表
C_list = ["萧炎","林动","牧尘"]
#定义生产者名称列表
P_list = ["阿猫","阿狗","阿猪"]
#定义线程列表
P_thread_list = []
#创建生产者线程
for p_name in P_list:
p = Produce(p_name,url_q,data_q)
p.start()
#创建消费者进程
for c_name in C_list:
c = Consumer(c_name,data_q)
c.start()
#主线程任务结束之后,进入阻塞状态,
# 一直等待其他的子线程执行结束之后,主线程在终止
for p_t in P_thread_list:
p_t.join()
#阻止主线程终止,子线程结束后,将flag标志置为True,线程中的循环方可退出
flag = True