1. 线程队列

1.1 先进先出(FIFO)

import queue

q = queue.Queue(3)
q.put('a')
q.put('b')
q.put('c') print(q.qsize()) # 队列大小
print(q.get())
print(q.get())
print(q.get())
print(q.get(timeout=2)) # 阻塞2s后报错
print(q.get(block=False)) # 阻塞后报错

1.2 后进先出(LIFO)堆栈

q = queue.LifoQueue(3)
q.put('a')
q.put('b')
q.put('c') print(q.qsize())
print(q.get())
print(q.get())
print(q.get())
"""
c
b
a
"""

1.3 优先级队列

# 自定义队列,数字越小优先级越高,元组形式
import queue q = queue.PriorityQueue(3)
q.put((0, 'a'))
q.put((-1, 'b'))
q.put((1, 'c')) print(q.qsize())
print(q.get())
print(q.get())
print(q.get())
"""
(-1, 'b')
(0, 'a')
(1, 'c')
"""

面试题:用列表实现队列和堆栈

lst = []
def add():
while 1:
msg = input('>>>').strip()
lst.append(msg)
if len(lst) > 3:
return lst def pop_lst(lst):
for i in range(len(lst)):
print(lst.pop(0)) # 队列
#print(lst.pop()) # 堆栈 lt = add()
pop_lst(lt) # 第二种加event from threading import Thread
from threading import Event event = Event()
lst = []
def add_queue():
while 1:
msg = input('>>>').strip()
lst.append(msg)
if len(lst) > 3:
event.set()
break def pop_queue(lst):
event.wait()
for i in range(len(lst)):
print(lst.pop()) # t1 = Thread(target=add_queue)
t2 = Thread(target=pop_queue,args=(lst,))
t1.start()
t2.start()

2. 事件event

​ 开启两个线程,一个线程运行到中间的某个阶段,触发另一个线程执行。两个线程增加了耦合性。

如果程序中的其他线程需要通过判断某个线程的状态来确定自己下一步的操作,就需要用threading中的Event模块。

# 一个线程监测服务器是否开启;另一个线程判断如果i哦开启,则能够连接成功,
# 此线程只能尝试连接三次,每次间隔1秒。
from threading import Thread
from threading import Event
from threading import current_thread
import time event = Event()
def check():
print(f"{current_thread().name}监测服务器是否开启...")
time.sleep(2)
event.set() # 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
print(f"{current_thread().name}服务器开启") def connect():
count = 1
while 1:
if count > 3:
print(f"{current_thread().name}超时,连接失败!")
break
event.wait(1) # 阻塞,直到event.set()方法之后,才会执行后面的代码;里面可以设置时间,1s后如果还没有event.set(),不等待,直接执行下一步。
print(f"{current_thread().name}等待连接第{count}次")
if event.is_set(): # 判断是否event.set()
print(f"{current_thread().name}连接成功!")
break
count += 1
t1 = Thread(target=ckeck, name="T1")
t2 = Thread(target=connect, name="T2")
t1.start()
t2.start()

3. 协程

并发的本质:切换 + 保持状态。

协程:是单线程下并发的处理任务,又称微线程、纤程。

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

过程:

开启协程并发执行,自己的程序把控着cpu在多个任务之间来回切换,并且保持状态。

协程切换速度非常快,蒙蔽了操作系统的监测,让操作系统认为cpu一直在运行你这个线程。(协程)

协程处理IO密集型效率高。

优点:

  1. 开销小;
  2. 运行速度快;
  3. 协程会长期霸占cpu,只执行程序里的所有任务,最大限度的利用cpu。

缺点:

  1. 本质是单线程下,无法利用多核;
  2. 一旦协程出现阻塞,将会阻塞整个线程,cpu也会被切走。

总结协程的特点

  1. 必须在只有一个单线程里实现并发;
  2. 修改共享数据不需加锁;
  3. 用户程序里自己保存多个控制流的上下文栈(能够保持状态);
  4. 一个协程的任务遇到IO操作自动切换到其他任务。

4. Greenlet 模块

from greenlet import greenlet

def eat(name):
print('%s eat 1' %name) #2
g2.switch('taibai') #3
print('%s eat 2' %name) #6
g2.switch() #7
def play(name):
print('%s play 1' %name) #4
g1.switch() #5
print('%s play 2' %name) #8 g1=greenlet(eat)
g2=greenlet(play)
g1.switch('taibai') #可以在第一次switch时传入参数,以后都不需要 1

​ 能够在任务之间直接切换,但是当切到一个任务执行时如果遇到io,那就会原地阻塞,仍然是没有解决遇到IO自动切换来提升效率的问题。

5. Gevent模块

第三方库,可以轻松通过gevent实现并发同步或异步编程,模式是Greenlet。特点就是,遇到IO阻塞就会自动切换任务

import gevent
import time
from gevent import monkey
monkey.patch_all() # 打补丁: 将下面的所有任务的阻塞都打上标记 def eat(name):
print('%s eat 1' %name)
time.sleep(2) # 阻塞,切换任务
print('%s eat 2' %name) def play(name):
print('%s play 1' %name)
time.sleep(1) # 阻塞,切换任务
print('%s play 2' %name) g1 = gevent.spawn(eat, 'meet')
g2 = gevent.spawn(play, 'meet')
gevent.joinall([g1,g2]) # 只有一个线程,因此需join
"""
meet eat 1
meet play 1
meet play 2
meet eat 2
""" # 如果两个任务同时遇到阻塞,切换一两次后操作系统会将cpu切走,程序挂起,当阻塞完毕后,会抢cpu,执行下面的代码。
一般在工作中我们都是进程+线程+协程的方式来实现并发,以达到最好的并发效果,如果是4核的cpu,一般起5个进程,每个进程中20个线程(5倍cpu数量),每个线程可以起500个协程,大规模爬取页面的时候,等待网络延迟的时间的时候,我们就可以用协程去实现并发。 并发数量 = 5 * 20 * 500 = 50000个并发,这是一般一个4cpu的机器最大的并发数。nginx在负载均衡的时候最大承载量就是5w个.
  单线程里的多个任务的代码通常会既有计算操作又有阻塞操作,我们完全可以在执行任务1时遇到阻塞,就利用阻塞的时间去执行任务2。。。。如此,才能提高效率,这就用到了Gevent模块。

python 38 线程队列与协程的更多相关文章

  1. 线程队列 concurrent 协程 greenlet gevent

    死锁问题 所谓死锁:是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进 ...

  2. python day 20: 线程池与协程,多进程TCP服务器

    目录 python day 20: 线程池与协程 2. 线程 3. 进程 4. 协程:gevent模块,又叫微线程 5. 扩展 6. 自定义线程池 7. 实现多进程TCP服务器 8. 实现多线程TCP ...

  3. Python 37 进程池与线程池 、 协程

    一:进程池与线程池 提交任务的两种方式: 1.同步调用:提交完一个任务之后,就在原地等待,等任务完完整整地运行完毕拿到结果后,再执行下一行代码,会导致任务是串行执行 2.异步调用:提交完一个任务之后, ...

  4. python 之 并发编程(线程Event、协程)

    9.14 线程Event connect线程执行到event.wait()时开始等待,直到check线程执行event.set()后立即继续线程connect from threading impor ...

  5. Python 多线程、进程、协程上手体验

    浅谈 Python 多线程.进程.协程上手体验 前言:浅谈 Python 很多人都认为 Python 的多线程是垃圾(GIL 说这锅甩不掉啊~):本章节主要给你体验下 Python 的两个库 Thre ...

  6. Python多进程、多线程、协程

    转载:https://www.cnblogs.com/huangguifeng/p/7632799.html 首先我们来了解下python中的进程,线程以及协程! 从计算机硬件角度: 计算机的核心是C ...

  7. python并发编程之asyncio协程(三)

    协程实现了在单线程下的并发,每个协程共享线程的几乎所有的资源,除了协程自己私有的上下文栈:协程的切换属于程序级别的切换,对于操作系统来说是无感知的,因此切换速度更快.开销更小.效率更高,在有多IO操作 ...

  8. Python PEP 492 中文翻译——协程与async/await语法

    原文标题:PEP 0492 -- Coroutines with async and await syntax 原文链接:https://www.python.org/dev/peps/pep-049 ...

  9. python并开发编程之协程

    一 引出协成 并发的本质是:切换+保存状态 CPU在运行行一个任务时,会在两种情况下切走去执行其他任务,一是该任务发生了阻塞,二是运行该任务的时间过长 yeild可以保存状态,yeild状态保存与操作 ...

随机推荐

  1. Redis(一)--- 概述

    使用redis很长时间了,但只是限于使用,一直也没有进行系统的学习与底层研究,现系统的学习一下redis并记录笔记. Redis 1.Nosql 非关系型数据库,key-value键值对存贮. 2.内 ...

  2. IO-Java实现文件的复制

    public class FileCopy { public static void main(String[] args) throws IOException { // 1.创建一个字节输入流对象 ...

  3. rabbitMQ_routing(四)

    路由 本次我们将通过路由将信息发送到指定的队列中,将消息发送给指定的队列需要在转发器和队列之间建立一个routeKey 绑定 在以前的例子中,我们已经创建了绑定.你可能会记得如下代码: channel ...

  4. On The Way—Step 1 :python入门之Python的历程

    1.python的历史 2004 Django框架 python2 和 python3的区别 python2 源码不统一 有重复功能代码 python3 源码统一 没有重复功能代码 Python的发展 ...

  5. Python装饰器 (转)

    多个装饰器执行的顺序就是从最后一个装饰器开始,执行到第一个装饰器,再执行函数本身. #多个装饰器 import time def deco01(func): def wrapper(*args, ** ...

  6. Charles(Windows/Android)入门使用

    一. 介绍以及下载(windows) Charles是一个HTTP代理/HTTP监视器/反向代理,使开发人员能够查看其机器和Internet之间所有HTTP和SSL/HTTPS流量,这包括请求,响应和 ...

  7. 跟着阿里p7一起学java高并发 - 第19天:JUC中的Executor框架详解1,全面掌握java并发核心技术

    这是java高并发系列第19篇文章. 本文主要内容 介绍Executor框架相关内容 介绍Executor 介绍ExecutorService 介绍线程池ThreadPoolExecutor及案例 介 ...

  8. maven的编译规范

    maven的中央仓库上的jar的包名必须小写.否则maven编译不通过. 如:Memcached-Java-Client-3.0.2 的jar包. 目录如下: com.whalin.memcached ...

  9. Gunicorn-Django部署

    1. 简单部署 1. sudo pip3 install gunicorn 2. cd 到django项目中 sudo python3 manage.py migrate 3.启动服务:sudo py ...

  10. 夯实Java基础(八)——代码块

    在Java中代码块指的是使用”{}”括起来的代码称为代码块.代码块一共分为4种:局部代码块,静态代码块,同步代码块,构造代码块. 1.局部代码块 局部代码块就是定义在方法体内部的代码块. public ...