Python之网络编程 非阻塞同异步 协程

## 阻塞 非阻塞 异步 同步

程序运行中表现得状态: 阻塞 运行 就绪

阻塞:程序遇到IO阻塞,立马停止(挂起), CPU马上切换, 等到IO结束之后 再执行

非阻塞: 程序没有遇到IO或者遇到IO通过某种手段让CPU去执行其他的任务,尽可能的占用CPU

异步,同步:

站在任务发布的角度:

同步:任务发布之后,等待,直到这个任务执行完毕, 给我一个返回值,我再发布下一个任

异步:所有的任务同时发出,我就继续执行下一行 之前的任务执行完毕 结果返回

```
# from concurrent.futures import ProcessPoolExecutor
# import os
# import time
# import random
#
# def task():
# print(f'{os.getpid()} is running')
# time.sleep(random.randint(0,2))
# return f'{os.getpid()} is finish'
#
# if __name__ == '__main__':
#
# p = ProcessPoolExecutor(4)
# obj_l1 = []
# for i in range(10):
# obj = p.submit(task,) # 异步发出.
# obj_l1.append(obj)
#
# # time.sleep(3)
# p.shutdown(wait=True)
# # 1. 阻止在向进程池投放新任务,
# # 2. wait = True 十个任务是10,一个任务完成了-1,直至为零.进行下一行.
# # print(666)
# for i in obj_l1:
# print(i.result())
# 异步回收任务的方式一: 我将所有的任务的结果统一收回.



# 同步发布任务: 我要发布10个任务,先把第一个任务给第一个进程,等到第一个进程完成之后.
# 我在将第二任务给了下一个进程,......

# 异步发布任务: 我直接将10个任务抛给4个进程, 我就继续执行下一行代码了.等结果.




# 同步:

# from concurrent.futures import ProcessPoolExecutor
# import os
# import time
# import random
#
# def task():
# print(f'{os.getpid()} is running')
# time.sleep(1)
# return f'{os.getpid()} is finish'
#
# if __name__ == '__main__':
#
# p = ProcessPoolExecutor(4)
#
# for i in range(10):
# obj = p.submit(task,) # 异步发出.
# print(obj.result())
```

## 异步 + 调用机制

```
# # 爬虫.
# # 1 简单认识一下requests模块
# # 第一步: 爬取服务端的文件(IO阻塞).
# # 第二步: 拿到文件,进行数据分析,(非IO,IO极少)
# import requests
# from concurrent.futures import ProcessPoolExecutor
# from multiprocessing import Process
# import time
# import random
# import os
#
# def get(url):
# response = requests.get(url)
# print(f'{os.getpid()} 正在爬取:{url}')
# time.sleep(random.randint(1,3))
# if response.status_code == 200:
# return response.text
#
#
# def parse(text):
# '''
# 对爬取回来的字符串的分析
# 简单用len模拟一下.
# :param text:
# :return:
# '''
# print(f'{os.getpid()} 分析结果:{len(text)}')
#
# # get('http://www.taobao.com')
# # get('http://www.baidu.com')
# # get('http://www.JD.com')
#
# if __name__ == '__main__':
#
# url_list = [
# 'http://www.taobao.com',
# 'http://www.JD.com',
# 'http://www.JD.com',
# 'http://www.JD.com',
# 'http://www.baidu.com',
# 'https://www.cnblogs.com/jin-xin/articles/11232151.html',
# 'https://www.cnblogs.com/jin-xin/articles/10078845.html',
# 'http://www.sina.com.cn',
# 'https://www.sohu.com',
# 'https://www.youku.com',
# ]
# pool = ProcessPoolExecutor(4)
# obj_list = []
# for url in url_list:
# obj = pool.submit(get, url)
# obj_list.append(obj)
#
# pool.shutdown(wait=True)
#
# for obj in obj_list:
# parse(obj.result())
'''
串行
obj_list[0].result()
obj_list[1].result()
obj_list[2].result()
obj_list[3].result()
obj_list[4].result()
'''

# 问题出在哪里?
# 1. 分析结果的过程是串行,效率低.
# 2. 你将所有的结果全部都爬取成功之后,放在一个列表中,分析.
# 问题1解决:
# 在开进程池,再开进程,耗费资源.

'''
爬取一个网页需要2s,并发爬取10个网页:2.多s.
分析任务: 1s. 10s. 总共12.多秒.

现在这个版本的过程:
异步发出10个爬取网页的任务,然后4个进程并发(并行)的先去完成4个爬取网页的任务,然后谁先结束,谁进行下一个
爬取任务,直至10个任务全部爬取成功.
将10个爬取结果放在一个列表中,串行的分析.

爬取一个网页需要2s,分析任务: 1s,总共3s,总共3.多秒(开启进程损耗).
. 10s.
下一个版本的过程:
异步发出10个 爬取网页+分析 的任务,然后4个进程并发(并行)的先去完成4个爬取网页+分析 的任务,
然后谁先结束,谁进行下一个 爬取+分析 任务,直至10个爬取+分析 任务全部完成成功.




'''

# 版本二:
# 异步处理: 获取结果的第二种方式: 完成一个任务返回一个结果,完成一个任务,返回一个结果 并发的返回.

# import requests
# from concurrent.futures import ProcessPoolExecutor
# from multiprocessing import Process
# import time
# import random
# import os
#
# def get(url):
# response = requests.get(url)
# print(f'{os.getpid()} 正在爬取:{url}')
# time.sleep(random.randint(1,3))
# if response.status_code == 200:
# parse(response.text)
#
#
# def parse(text):
# '''
# 对爬取回来的字符串的分析
# 简单用len模拟一下.
# :param text:
# :return:
# '''
# print(f'{os.getpid()} 分析结果:{len(text)}')
#
# if __name__ == '__main__':
#
# url_list = [
# 'http://www.taobao.com',
# 'http://www.JD.com',
# 'http://www.JD.com',
# 'http://www.JD.com',
# 'http://www.baidu.com',
# 'https://www.cnblogs.com/jin-xin/articles/11232151.html',
# 'https://www.cnblogs.com/jin-xin/articles/10078845.html',
# 'http://www.sina.com.cn',
# 'https://www.sohu.com',
# 'https://www.youku.com',
# ]
# pool = ProcessPoolExecutor(4)
# for url in url_list:
# obj = pool.submit(get, url)
#
# # pool.shutdown(wait=True)
# print('主')


# 版本三: 版本二几乎完美,但是两个任务有耦合性. 再上一个基础上,对其进程解耦.
# 回调函数

# import requests
# from concurrent.futures import ProcessPoolExecutor
# from multiprocessing import Process
# import time
# import random
# import os
#
# def get(url):
# response = requests.get(url)
# print(f'{os.getpid()} 正在爬取:{url}')
# # time.sleep(random.randint(1,3))
# if response.status_code == 200:
# return response.text
#
#
# def parse(obj):
# '''
# 对爬取回来的字符串的分析
# 简单用len模拟一下.
# :param text:
# :return:
# '''
# time.sleep(1)
# print(f'{os.getpid()} 分析结果:{len(obj.result())}')
#
# if __name__ == '__main__':
#
# url_list = [
# 'http://www.taobao.com',
# 'http://www.JD.com',
# 'http://www.JD.com',
# 'http://www.JD.com',
# 'http://www.baidu.com',
# 'https://www.cnblogs.com/jin-xin/articles/11232151.html',
# 'https://www.cnblogs.com/jin-xin/articles/10078845.html',
# 'http://www.sina.com.cn',
# 'https://www.sohu.com',
# 'https://www.youku.com',
# ]
# start_time = time.time()
# pool = ProcessPoolExecutor(4)
# for url in url_list:
# obj = pool.submit(get, url)
# obj.add_done_callback(parse) # 增加一个回调函数
# # 现在的进程完成的还是网络爬取的任务,拿到了返回值之后,结果丢给回调函数add_done_callback,
# # 回调函数帮助你分析结果
# # 进程继续完成下一个任务.
# pool.shutdown(wait=True)
#
# print(f'主: {time.time() - start_time}')

# 回调函数是主进程帮助你实现的, 回调函数帮你进行分析任务. 明确了进程的任务: 只有一个网络爬取.
# 分析任务: 回调函数执行了.对函数之间解耦.

# 极值情况: 如果回调函数是IO任务,那么由于你的回调函数是主进程做的,所以有可能影响效率.

# 回调不是万能的,如果回调的任务是IO,
# 那么异步 + 回调机制 不好.此时如果你要效率只能牺牲开销,再开一个线程进程池.


# 异步就是回调! 这个是错的!! 异步,回调是两个概念.

# 如果多个任务,多进程多线程处理的IO任务.
# 1. 剩下的任务 非IO阻塞. 异步 + 回调机制
# 2. 剩下的任务 IO << 多个任务的IO 异步 + 回调机制
# 3. 剩下的任务 IO >= 多个任务的IO 第二种解决方式,或者两个进程线程池.
```

## 线程队列

```
# 1 FIFO queue

# import queue
#
# q = queue.Queue(3)
# q.put(1)
# q.put(2)
# q.put('太白')
# # q.put(666)
#
# print(q.get())
# print(q.get())
# print(q.get())


# LIFO 栈.
# import queue
#
# q = queue.LifoQueue()
# q.put(1)
# q.put(3)
# q.put('barry')
#
# print(q.get())
# print(q.get())
# print(q.get())
# print(q.get())


# 优先级队列
# 需要元组的形式,(int,数据) int 代表优先级,数字越低,优先级越高.
# import queue
# q = queue.PriorityQueue(3)
#
# q.put((10, '垃圾消息'))
# q.put((-9, '紧急消息'))
# q.put((3, '一般消息'))
#
# print(q.get())
# print(q.get())
# print(q.get())
```

## 事件 Event

```
# import time
# from threading import Thread
# from threading import current_thread
#
# flag = False
#
#
# def task():
# print(f'{current_thread().name} 检测服务器是否正常开启....')
# time.sleep(3)
# global flag
# flag = True
#
#
# def task1():
# while 1:
# time.sleep(1)
# print(f'{current_thread().name} 正在尝试连接服务器.....')
# if flag:
# print('连接成功')
# return
#
# if __name__ == '__main__':
# t1 = Thread(target=task1,)
# t2 = Thread(target=task1,)
# t3 = Thread(target=task1,)
#
# t = Thread(target=task)
#
#
# t.start()
# t1.start()
# t2.start()
# t3.start()



import time
from threading import Thread
from threading import current_thread
from threading import Event

event = Event() # 默认是False
def task():
print(f'{current_thread().name} 检测服务器是否正常开启....')
time.sleep(3)
event.set() # 改成了True

def task1():
print(f'{current_thread().name} 正在尝试连接服务器')
# event.wait() # 轮询检测event是否为True,当其为True,继续下一行代码. 阻塞.
event.wait(1)
# 设置超时时间,如果1s中以内,event改成True,代码继续执行.
# 设置超时时间,如果超过1s中,event没做改变,代码继续执行.
print(f'{current_thread().name} 连接成功')
if __name__ == '__main__':
t1 = Thread(target=task1,)
t2 = Thread(target=task1,)
t3 = Thread(target=task1,)

t = Thread(target=task)


t.start()
t1.start()
t2.start()
t3.start()
```

## 协程初识



## 协程

```
# # import time
# #
# # def func1():
# #
# # for i in range(11):
# # yield
# # print('这是我第%s次打印啦' % i)
# # time.sleep(1)
# #
# #
# # def func2():
# # g = func1()
# # #next(g)
# # for k in range(10):
# #
# # print('哈哈,我第%s次打印了' % k)
# # time.sleep(1)
# # next(g)
#
# #不写yield,下面两个任务是执行完func1里面所有的程序才会执行func2里面的程序,
# # 有了yield,我们实现了两个任务的切换+保存状态
# # func1()
# # func2()
#
#
# # 计算密集型:串行与协程的效率对比
# # import time
# #
# # def task1():
# # res = 1
# # for i in range(1,100000):
# # res += i
# #
# #
# # def task2():
# # res = 1
# # for i in range(1,100000):
# # res -= i
# #
# # start_time = time.time()
# # task1()
# # task2()
# # print(f'串行消耗时间:{time.time()-start_time}') # 串行消耗时间:0.012000560760498047
#
#
# import time
#
#
# def task1():
# res = 1
# for i in range(1, 100000):
# res += i
# yield res
#
#
# def task2():
# g = task1()
# res = 1
# for i in range(1, 100000):
# res -= i
# next(g)
#
#
# start_time = time.time()
# task2()
# print(f'协程消耗时间:{time.time() - start_time}') # 协程消耗时间:0.0260012149810791

#
# from greenlet import greenlet
# import time
#
# # 不能自动切换,
# # 遇到IO不切换
# # 可以保持原来的状态.
# def eat(name):
#
# print('%s eat 1' %name) #2
# g2.switch('alex') #3
# time.sleep(3)
# print('%s eat 2' %name) #6
# g2.switch() #7
#
# def play(name):
# print('%s play 3' %name) #4
# g1.switch() #5
# print('%s play 4' %name) #8
#
# g1 = greenlet(eat)
# g2 = greenlet(play)
#
# g1.switch('太白') # 1 第一次切换一定要传参
#
# # g2.switch('b1')
#
# # time.sleep(300)

# 还没有做到真正遇到IO切换
#
# import gevent
# import time
# def eat(name):
# print('%s eat 1' %name) # 1
# # gevent.sleep(2)
# time.sleep(300)
# print('%s eat 2' %name)
#
# def play(name):
# print('%s play 1' %name) # 2
# # gevent.sleep(1)
# time.sleep(3)
# print('%s play 2' %name)
#
#
# g1 = gevent.spawn(eat, 'alex')
# g2 = gevent.spawn(play, name='taibai')
# # g1.join()
# # g2.join()
# #或者gevent.joinall([g1,g2])
# gevent.joinall([g1,g2])
# print('主')



import threading
from gevent import monkey
monkey.patch_all() # 将你代码中的所有的IO都标识.

import gevent # 直接导入即可
import time
def eat():
print(f'线程1:{threading.current_thread().getName()}')
print('eat food 1')
time.sleep(3) # 加上mokey就能够识别到time模块的sleep了
print('eat food 2')

def play():
print(f'线程2:{threading.current_thread().getName()}')
print('play 1')
time.sleep(1) # 来回切换,直到一个I/O的时间结束,这里都是我们个gevent做得,不再是控制不了的操作系统了。
print('play 2')

g1=gevent.spawn(eat)
g2=gevent.spawn(play)
gevent.joinall([g1,g2])
print(f'主:{threading.current_thread().getName()}')
```

转载于:https://www.cnblogs.com/Jacob-yang/p/11391694.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值