同步/异步 异步回调 协成 线程队列

目录:

    同步/异步

    异步回调

    协成

    线程队列

同步|异步:

线程的三种状态:
  1.就绪
  2.运行
  3.阻塞
阻塞和非阻塞描述的是运行的状态
阻塞 :遇到了IO操作,代码卡住,无法执行下一行,CPU会切换到其他任务
非阻塞 :与阻塞相反,代码正在执行(运行状态) 或处于就绪状态

同步和异步指的是提交任务的方式
同步 :提交任务必须等待任务完成,才能执行下一行
异步 :提交任务不需要等待任务完成,立即执行下一行

代码:

 1 def task():
 2     for i in range(1000000):
 3         i += 1000
 4     print("11111")
 5
 6 print("start")
 7 task() # 同步提交方式,等函数运行完菜执行下一行
 8 print("end")
 9
10 from threading import  Thread
11
12 print("start1")
13 Thread(target=task).start() # 异步提交,开启线程,然后去执行之后的代码,线程内代码自行执行
14 print("end1")

异步回调:任务执行结束后自动调用某个函数

异步回调:  在发起异步任务后,子进程或子线程完成任务后需要通知任务发起方.通过调用一个函数,all_done_callback(函数名)为什么需要回调?
    子进程帮助主进程完成任务,处理任务的结果应该交还给主进程
其他方式也可以将数据交还给主进程
    1.shutdown  主进程会等到所有任务完成
    2.result函数 会阻塞直到任务完成
    都会阻塞,导致效率降低,所以使用回调
注意:
    回调函数什么时候被执行? 子进程任务完成时
    谁在执行回调函数? 主进程
线程的异步回调
    使用方式都相同,唯一的不同是执行回调函数,是子线程在执行(线程间数据共享)

三种方式:

 1 # 方式1 自己来保存数据 并执行shutdown  仅在多线程
 2
 3 res = []
 4 def task():
 5     print("%s is 正在打水" % os.getpid())
 6     time.sleep(0.2)
 7     w = "%s 打的水" % os.getpid()
 8     res.append(w)
 9     return w
10
11 if __name__ == ‘__main__‘:
12     for i in range(20):
13         # 提交任务会返回一个对象  用于回去执行状态和结果
14         f = pool.submit(task)
15         print(f.result()) # 方式2   执行result 它是阻塞的直到任务完成  又变成串行了
16
17     print("11111")
18     # pool.shutdown() # 首先不允许提交新任务 然后等目前所有任务完成后
19     # print(res)
20     print("over")
21
22 ====================================================================================
23
24 pool = ThreadPoolExecutor()
25
26 # 方式3 通过回调(什么是回调 任务执行结束后自动调用某个函数)
27 def task():
28     print("%s is 正在打水" % os.getpid())
29     # time.sleep(0.2)
30     w = "%s 打的水" % os.getpid()
31     return w
32
33 def task_finish(res):
34     print("打水完成! %s" % res)
35
36 if __name__ == ‘__main__‘:
37     for i in range(20):
38         # 提交任务会返回一个对象  用于回去执行状态和结果
39         f = pool.submit(task)
40         f.add_done_callback(task_finish) #添加完成后的回调
41     print("11111")
42     print("over")

利用回调完成生产者消费者:

多进程:

1 from  concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
 2 from threading import current_thread
 3 import  os
 4 # 进程池
 5 pool = ProcessPoolExecutor()
 6 # 爬虫:从网络某个地址获取一个HTML文件
 7 import requests # 该模块用于网络(HTTP)请求
 8
 9 # 生产数据,即生产者
10 def get_data_task(url):
11     print(os.getpid(),"正在生产数据!")
12     # print(current_thread(),"正在生产数据!")
13
14     response = requests.get(url)
15     text = response.content.decode("utf-8")
16     print(text)
17     return text
18
19 # 处理数据,即消费者
20 def parser_data(f):
21     print(os.getpid(),"处理数据")
22     # print(current_thread(), "处理数据")
23     print("正在解析: 长度%s" % len(f.result()))
24
25 urls = [
26     "http://www.baidu.com",
27     "http://www.baidu.com",
28     "http://www.baidu.com",
29     "http://www.baidu.com"
30 ]
31
32 if __name__ == ‘__main__‘:
33     for url in urls:
34         f = pool.submit(get_data_task,url)
35         f.add_done_callback(parser_data)  # 回调函数是主进程在执行
36         # 因为子进程是负责获取数据的,然而数据怎么处理 ,子进程并不知道.应该把数据还给主进程
37     print("over")

多线程:

 1 from  concurrent.futures import ThreadPoolExecutor
 2 from threading import current_thread
 3 # 进程池
 4 pool = ThreadPoolExecutor()
 5
 6 # 爬虫:从网络某个地址获取一个HTML文件
 7 import requests # 该模块用于网络(HTTP)请求
 8
 9 # 生产数据
10 def get_data_task(url):
11     # print(os.getpid(),"正在生产数据!")
12     print(current_thread(),"正在生产数据!")
13
14     response = requests.get(url)
15     text = response.content.decode("utf-8")
16     print(text)
17     return text
18
19 #   处理数据
20 def parser_data(f):
21     # print(os.getpid(),"处理数据")
22     print(current_thread(), "处理数据")
23     print("正在解析: 长度%s" % len(f.result()))
24
25 urls = [
26     "http://www.baidu.com",
27     "http://www.baidu.com",
28     "http://www.baidu.com",
29     "http://www.baidu.com"
30 ]
31
32 if __name__ == ‘__main__‘:
33     for url in urls:
34         f = pool.submit(get_data_task,url)
35         f.add_done_callback(parser_data)  # 回调函数是主进程在执行
36         # 因为子进程是负责获取数据的  然而数据怎么处理 子进程并不知道  应该把数据还给主进程
37     print("over")

线程队列:

普通队列/堆栈队列/优先级队列:

import queue

# 普通队列 先进先出
q = queue.Queue()
q.put("a")
q.put("b")

print(q.get())
print(q.get())

# 堆栈队列  先进后出 后进先出  函数调用就是进栈  函数结束就出栈 递归造成栈溢出
q2 = queue.LifoQueue()
q2.put("a")
q2.put("b")
print(q2.get())

# 优先级队列
q3 = queue.PriorityQueue()  # 数值越小优先级越高  优先级相同时 比较大小 小的先取
q3.put((-100, "c"))
q3.put((1, "a"))
q3.put((100, "b"))
print(q3.get())

协程:在单线程下由应用程序级别实现并发

什么是协程?
    协程指的是单线程下由应用程序级别实现的并发
    即把本来由操作系统控制的切换+保存状态,在应用
    程序里实现了

    协程的切换vs操作系统的切换
    优点:
        切换速度远快于操作系统
    缺点:
        一个任务阻塞了,其余的任务都无法执行
    ps:只有遇到io才切换到其他任务的协程才能提升
        单线程的执行效率

为何用协程?
    把单个线程的io降到最低,最大限度地提升单个线程的执行效率

如何实现协程?
    from gevent import spawn,monkey;monkey.patch_all()

协程的目的是在单线程下实现并发
为什么出现协程? 因为cpython中,由于GIL而导致同一时间只有一个线程在跑
意味着:如果你的程序时计算密集,多线程效率也不会提升
      如果是io密集型 没有必要在单线程下实现并发,我会开启多线程来处理io,子线遇到io,cpu切走.
      不能保证一定切到主线
      如果可以,我在遇到io的时候转而去做计算,这样一来可以保证cpu一直在处理你的程序,当然处理时间太长也要切走

 总结:单线程下实现并发,是将io阻塞时间用于执行计算,可以提高效率
 原理:一直使用CPU直到超时
怎么实现单线程并发?
    并发:指的是看起来像是同时运行,实际是在任务间来回切换,同时需要保存执行的状态
    任务一堆代码  可以用函数装起来
    1.如何让两个函数切换执行
        yield可以保存函数的执行状态
        通过生成器可以实现伪并发
        并发不一定提升效率,当任务全是计算时,反而会降低效率
    2.如何知道发生了io, 从而切换执行?
        第三方模块,gevent
第三方模块 greenlet 可以实现并发 但是不能检测io
第三方模块 gevent 封装greenlet 可以实现单线程并发,并且能够检测io操作,自动切换
协程的应用场景:    TCP 多客户端实现方式    1.来一个客户端就来一个进程     资源消耗较大    2.来一个客户端就来一个线程     也不能无限开    3.用进程池 或 线程池  还是一个线程或进程只能维护一个连接    4.协程 一个线程就可以处理多个客户端  遇到io就切到另一个

协成实现:单线程实现并发

1.yield 把函数做成生成器,生成器会自动保存状态

 1 # 这是一个进程,默认包含一个主线程
 2 import time  #生成器函数
 3 def task():
 4     while True:
 5         print("task1")
 6         time.sleep(1)#I/O,CPU切走
 7         yield 1
 8
 9 def task2():
10     g = task()
11     while True:
12         try:
13             print("task2")
14             next(g)#next()函数参数传一个可迭代对象
15         except Exception:
16             print("任务完成")
17             break
18 task2()
19 打印结果:
20     task2
21     task1
22     task2
23     task1
24     task2
25     task1
26     ..........

2.greenlet模块:帮我们封装yield,可以实现任务切换,但是不能检测I/O

# 1.实例化greenlet得到一个对象,传入要执行的任务,至少需要两个任务
# 2.先让某个任务执行起来,使用对象调用switch
# 3.在任务的执行过程中,手动调用switch来切换

 1 import greenlet
 2 import time
 3 def task1():
 4     print("task1 1")
 5     time.sleep(2)
 6     g2.switch()
 7     print("task1 2")
 8     g2.switch()
 9
10 def task2():
11     print("task2 1")
12     g1.switch()
13     print("task2 2")
14
15 g1 = greenlet.greenlet(task1)
16 g2 = greenlet.greenlet(task2)
17
18 g1.switch()

3.gevent:在greenlet的基础上封装检测io操作,自动切换

# 1.spawn函数传入你的任务
# 2.调用join 去开启任务
# 3.检测io操作需要打monkey补丁,就是一个函数,在程序最开始的地方调用它

 1 from gevent import monkey
 2 monkey.patch_all()
 3
 4 import gevent
 5 import time
 6 def eat():
 7     print(‘eat food 1‘)
 8     time.sleep(2)
 9     print(‘eat food 2‘)
10
11 def play():
12     print(‘play 1‘)
13     time.sleep(1)
14     print(‘play 2‘)
15
16 g1=gevent.spawn(eat)
17 g2=gevent.spawn(play)
18
19 gevent.joinall([g1,g2])
20 print(‘主‘)

协程实现TCP:

服务端:

 1 import gevent
 2 from gevent import monkey
 3 monkey.patch_all()
 4 import socket
 5
 6 server = socket.socket()
 7 # 重用端口
 8 server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
 9
10 server.bind(("127.0.0.1",9999))
11
12 server.listen(5)
13 def data_handler(conn):
14     print("一个新连接..")
15     while True:
16         data = conn.recv(1024)
17         conn.send(data.upper())
18
19 while True:
20     conn,addr = server.accept()
21     # 切到处理数据的任务去执行
22     gevent.spawn(data_handler,conn)

原文地址:https://www.cnblogs.com/wyf2019/p/11047164.html

时间: 2024-11-08 20:43:06

同步/异步 异步回调 协成 线程队列的相关文章

协程,事件,队列,同步,异步,回调函数

协程 什么是协成?单个线程并发的处理多个任务,程序控制协成的切换+保持状态,协成的切换速度非常快,蒙蔽了操作系统的眼睛,让操作系统认为CPU一直在运行 进程或线程都是由操作系统控制CPU来回切换,遇到阻塞就切换执行其他任务,协成是程序控制的,霸占CPU执行任务,会在操作系统控制CPU之前来回切换,操作系统就认为CPU一直在运作 协程的优点: 1.开销小 2.运行速度快 3.协程会长期霸占CPU只执行我程序里的所有任务 协程的缺点: 1.协程属于微并发,处理任务不易过多 2.协程的本质是单线程,无

Python 中的进程、线程、协程、同步、异步、回调

进程和线程究竟是什么东西?传统网络服务模型是如何工作的?协程和线程的关系和区别有哪些?IO过程在什么时间发生? 在刚刚结束的 PyCon2014 上海站,来自七牛云存储的 Python 高级工程师许智翔带来了关于 Python 的分享<Python中的进程.线程.协程.同步.异步.回调>. 一.上下文切换技术 简述 在进一步之前,让我们先回顾一下各种上下文切换技术. 不过首先说明一点术语.当我们说"上下文"的时候,指的是程序在执行中的一个状态.通常我们会用调用栈来表示这个状

# 进程/线程/协程 # IO:同步/异步/阻塞/非阻塞 # greenlet gevent # 事件驱动与异步IO # Select\Poll\Epoll异步IO 以及selectors模块 # Python队列/RabbitMQ队列

1 # 进程/线程/协程 2 # IO:同步/异步/阻塞/非阻塞 3 # greenlet gevent 4 # 事件驱动与异步IO 5 # Select\Poll\Epoll异步IO 以及selectors模块 6 # Python队列/RabbitMQ队列 7 8 ############################################################################################## 9 1.什么是进程?进程和程序之间有什么

异步调用,线程队列,时间,协程

异步的使用场景 爬虫: 1.从目标站点下载网页数据,本质是HTML格式字符串 2.用re从字符串中提取出你所需要的数据 #使用进程池 from concurrent.futures import ProcessPoolExecutor import requests,re,os def get_data(url): print('%s正在请求%s'%(os.getpid(),url)) response = requests.get(url) #获取网页数据包含报头 print('%s请求%s成

python网络编程基础(线程与进程、并行与并发、同步与异步)

python网络编程基础(线程与进程.并行与并发.同步与异步) 目录 线程与进程 并行与并发 同步与异步 线程与进程 进程 前言 进程的出现是为了更好的利用CPU资源使到并发成为可能. 假设有两个任务A和B,当A遇到IO操作,CPU默默的等待任务A读取完操作再去执行任务B,这样无疑是对CPU资源的极大的浪费.聪明的老大们就在想若在任务A读取数据时,让任务B执行,当任务A读取完数据后,再切换到任务A执行.注意关键字切换,自然是切换,那么这就涉及到了状态的保存,状态的恢复,加上任务A与任务B所需要的

同步和异步回调

同步和异步回调 作者:havoc (原文地址:http://blog.ometer.com/2011/07/24/callbacks-synchronous-and-asynchronous/) 这里讲两个使用callback设计API的指南,并且添加到我的杂记posts about minor API design points中.我之前多次在不同的场合发起过关于"sync vs. async"回调的问题.这个问题着实困扰着API设计者和使用者. 最近,这个问题在我处理Hammers

python 之 并发编程(进程池与线程池、同步异步阻塞非阻塞、线程queue)

9.11 进程池与线程池 池子使用来限制并发的任务数目,限制我们的计算机在一个自己可承受的范围内去并发地执行任务 池子内什么时候装进程:并发的任务属于计算密集型 池子内什么时候装线程:并发的任务属于IO密集型 进程池: from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor import time,os,random ? def task(x): print('%s 接客' %os.getpid()) time.

ios多线程中 同步、异步与队列之间的关系

同步和异步 异步: 指的就是多线程, 把`对应的代码`放到其他线程中去执行, 当前线程的代码就继续往下执行,不需要等到刚才放到子线程中的代码执行完毕. 同步执行函数: 这个实际的应用场景不多 , 了解即可 登录,注册 需要放到一个队列中去执行 下载文件 需要放到另外一个队列去执行 异步执行函数: 串行队列特点: 如果要开启线程,只会开启一条线程 (同步函数执行时, 如果队列是串行队列,不需要开启线程, 任务在当前线程中执行) (异步函数执行时, 如果队列是串行队列,可以开启线程,并且只能开启一条

使用C++11 开发一个半同步半异步线程池

摘自:<深入应用C++11>第九章 实际中,主要有两种方法处理大量的并发任务,一种是一个请求由系统产生一个相应的处理请求的线程(一对一) 另外一种是系统预先生成一些用于处理请求的进程,当请求的任务来临时,先放入同步队列中,分配一个处理请求的进程去处理任务, 线程处理完任务后还可以重用,不会销毁,而是等待下次任务的到来.(一对多的线程池技术) 线程池技术,能避免大量线程的创建和销毁动作,节省资源,对于多核处理器,由于线程被分派配到多个cpu,会提高并行处理的效率. 线程池技术分为半同步半异步线程