回调函数 协程

阻塞,非阻塞,

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

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

非阻塞: 程序没有IO或者 遇到IO通过某种手段让cpu去执行其他的任务,尽可能的占用cpu.(协程 使操作系统以为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())#有阻塞 必须运行完 才下一个   串行

异步+ 调用机制

#
# 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(1)
#     if response.status_code == 200:
#         return response.text
#
#
# def parse(obj):
#     '''
#     对爬取回来的字符串的分析
#     简单用len模拟一下.
#     :param text:
#     :return:
#     '''
#     time.sleep(2)
#     # 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(2)
#     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}')#22.37251353263855

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

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

# 回调不是万能的,如果回调的任务是IO,
# 那么异步 + 回调机制 不好.此时如果你要效率只能牺牲开销,再开一个线程进程池.
第一版
异步发出10个爬取网页的任务,然后4个进程并发(并行)的先去完成4个爬取网页的任务,然后谁先结束,谁进行下一个
爬取任务,直至10个任务全部爬取成功.
将10个爬取结果放在一个列表中,串行的分析.
先并发 在串行
第2版
异步(并发)处理: 获取结果的第二种方式: 完成一个任务返回一个结果,完成一个任务,返回一个结果 并发的返回.
4个线程同时做2件事 最快
第3版
增加一个回调函数 对象调用方法 括号里面是 函数名  谁调传谁
 现在的进程完成的还是网络爬取的任务,拿到了返回值之后,结果丢给回调函数add_done_callback,
# 回调函数帮助你分析结果 串行 和第1版时间查不了多少
# 进程继续完成下一个任务



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

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

线程队列

FIFO queue队列 # q = queue.Queue(3)

LIFO 栈. q = queue.LifoQueue()

优先级队列 q = queue.PriorityQueue(3) int 代表优先级,数字越低,优先级越高.

# 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

并发的执行某个任务 .多线程多进程,几乎同时执行.一个线程执行到中间时遇到条件 通知另一个线程开始执行.

event = Event() # 默认是False

event.set() # 改成了True 修改

event.wait() # 轮询检测event是否为True,当其为True,继续下一行代码. 阻塞. event.wait(1) 1s中以内,event改成True,代码立马执行.超过1s中,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()

协程总结

计算型 的协成慢 串行快

协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。、

协程: 微并发 协程它会调度cpu,如果协程管控的任务中,遇到阻塞,它会快速的(比操作系统快)切换到另一个任务,并且能将上一个任务挂起(保持状态,),让操作系统以为cpu一直在工作.

优点

#1. 协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
#2. 单线程内就可以实现并发的效果,最大限度地利用cpu
#3. 修改共享数据不需加锁

缺点

#1. 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程
#2. 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

协程就是主线程干活

import threading
from gevent import monkey
monkey.patch_all()  # 上面2行将你代码中的所有的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])##主线程 要等我g1g2完成在结束 因为要用主线程 做协程
print(f'主:{threading.current_thread().getName()}')

多线程并发: 一个进程如果要是开4个线程,最多可以处理比如(30)个任务.

多协程并发: 一个进程开启4个线程,然后我将4个线程设置4个协程,每个协程可以执行比如(30个任务.120个任务).(了解)

转载于:https://www.cnblogs.com/saoqiang/p/11377453.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值