day38

线程queue

多线程抢占资源

只能让其串行——用到互斥锁

线程queue
  • 队列——先进先出(FIFO)
import queue
q = queue.Queue(3)
q.put(1)
q.put(2)
q.put(3)
# q.put(4) # 阻塞等其他进程或者线程来拿
print(q.get())
print(q.get())
print(q.get())
# print(q.get(block=False)) # 没有值就直接报错
# q.get(timeout=2) # 阻塞2s,还没有值直接报错
  • 堆栈——先进后出(LIFO)

import queue
q = queue.LifoQueue(4)
q.put(1)
q.put(2)
q.put("alex")
q.put("太白")
print(q.get())
print(q.get())
print(q.get())
print(q.get())
结果:
太白
alex
2
1
  • 优先级队列——自己设置优先级
import queue
q = queue.PriorityQueue(4)
q.put((5, "元宝"))
q.put((-2, "狗狗"))
q.put((0, "2李业"))
q.put((0, "1刚哥"))
print(q.get())
print(q.get())
print(q.get()) # 数字越小就先出去,相同数字按照asill码来排序

事件event

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

版本一
from threading import Thread
from threading import current_thread
import time flag = False def check():
print(f"{current_thread().name} 监测服务器是否开启。。。")
time.sleep(3)
global flag
flag = True
print("服务器已经开启。。。") def connect():
while 1:
print(f"{current_thread().name} 等待连接。。。")
time.sleep(0.5)
if flag:
print(f"{current_thread().name} 连接成功。。。")
break t1 = Thread(target=check)
t2 = Thread(target=connect)
t1.start()
t2.start()
版本二——事件event
from threading import Thread
from threading import current_thread
from threading import Event
import time event = Event() def check():
print(f"{current_thread().name} 监测服务器是否开启。。。")
time.sleep(3)
# print(event.is_set())
event.set()
# print(event.is_set())
print("服务器已经开启。。。") def connect():
print(f"{current_thread().name} 等待连接。。。")
event.wait() # 阻塞直到event.set() 方法之后
# event.wait(1) # 只阻塞1s,1s之后如果还没有进行set 直接进行下一步操作
print(f"{current_thread().name} 连接成功。。。") t1 = Thread(target=check)
t2 = Thread(target=connect)
t1.start()
t2.start()

要求:一个线程监测服务器是否开始,另一个线程判断如果开始了,则显示连接成功,此线程只尝试连接3次,1s一次,如果超过3次,还没有连接成功,则显示连接失败

from threading import Thread
from threading import current_thread
from threading import Event
import time event = Event() def check():
print(f"{current_thread().name} 监测服务器是否开启")
time.sleep(2)
event.set() def connect():
print(f"{current_thread().name} 等待连接,,,")
for i in range(3):
event.wait(1)
if event.is_set():
print("服务器已经开启")
print(f"{current_thread().name} 连接成功")
break
else:
print(f"{current_thread().name} 连接失败{i+1}次") t1 = Thread(target=check)
t2 = Thread(target=connect)
t1.start()
t2.start()

协程

协程详情:https://www.cnblogs.com/jin-xin/articles/11245654.html

一个线程并发的处理任务

  • 串行:一个线程执行一个任务,执行完毕之后,执行下一个任务
  • 并行:多个CPU执行多个任务,4个CPU执行4个任务
  • 并发:一个CPU执行多个任务,看起来像是同时运行

并发真正的核心:切换CPU+保持状态

多线程的并发:3个线程处理10个任务,如果线程1处理的这个任务遇到阻塞,CPU被操作系统切换到另一个线程

一个线程能否并发的处理任务??

一个线程处理三个任务

单个CPU:10个任务,让你给我并发执行这10个任务:

  • 方式一:开启多进程并发执行,操作系统切换+保持状态
  • 方式二:开启多线程并发执行,操作系统切换+保持状态
  • 方式三:开启协程并发的执行,自己的程序把控着CPU在3个任务之间来回切换+保持状态

对3详细解释:协程他切换速度非常快,蒙蔽操作系统的眼睛,让操作系统认为CPU一直再运行你这个线程(协程)

协程方式最好,为什么?

优点:

  • 开销小
  • 运行速度快
  • 协程会长期霸占CPU只执行我程序里面的所有任务

缺点:

协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程

协程处理IO密集型好,计算密集型还是串行好

什么是协程?

单个线程并发的处理多个任务,程序控制协程的切换+保持状态

协程的特点
  • 必须在只有一个单线程里实现并发
  • 修改共享数据不需加锁
  • 用户程序里自己保存多个控制流的上下文栈(保持状态)
  • 一个协程遇到IO会自动切换到其他任务
工作中

一般在工作中我们都是进程+线程+协程的方式来实现并发,以达到最好的并发效果,如果是4核的cpu,一般起5个进程,每个进程中20个线程(5倍cpu数量),每个线程可以起500个协程,大规模爬取页面的时候,等待网络延迟的时间的时候,我们就可以用协程去实现并发。 并发数量 = 5 * 20 * 500 = 50000个并发,这是一般一个4cpu的机器最大的并发数。nginx在负载均衡的时候最大承载量就是5w个单线程里的这20个任务的代码通常会既有计算操作又有阻塞操作,我们完全可以在执行任务1时遇到阻塞,就利用阻塞的时间去执行任务2。。。。如此,才能提高效率,这就用到了Gevent模块。

之前学的代码有没有切换:
def func1():
print("in func1") def func2():
print("in func2")
func1()
print("end") func2()
切换 + 保持状态:遇到IO不会主动切换
def gen():
while 1:
yield 1
print(333) def func():
obj = gen()
for i in range(10):
print(next(obj)) func()
greenlet——协程底层技术
from greenlet import greenlet
import time def eat(name):
print(f"{name} eat 1") # 2
g2.switch("taibai") # 3
# time.sleep(3)
print(f"{name} eat 2") # 6
g2.switch() # 7 def play(name):
print(f"{name} play 1") # 4
g1.switch() # 5
print(f"{name} play 2") # 8 g1 = greenlet(eat)
g2 = greenlet(play) g1.switch("taibai") # 切换到eat任务 1
协程low版
  • 模拟的阻塞
import gevent
import time
from threading import current_thread def eat(name):
print(f"{name} eat 1") # 2
print(current_thread().name) # 3
gevent.sleep(2)
# time.sleep(2)
print(f"{name} eat 2") # 7 def play(name):
print(f"{name} play 1") # 4
print(current_thread().name) # 5
gevent.sleep(1)
# time.sleep(1)
print(f"{name} play 2") # 6 g1 = gevent.spawn(eat, "egon")
g2 = gevent.spawn(play, "egon")
print(f"主{current_thread().name}") # 1
g1.join()
g2.join()
结果:
主MainThread
egon eat 1
MainThread
egon play 1
MainThread
egon play 2
egon eat 2
  • 真正的阻塞
import gevent
import time
from threading import current_thread def eat(name):
print(f"{name} eat 1") # 2
print(current_thread().name) # 3
# gevent.sleep(2)
time.sleep(2)
print(f"{name} eat 2") # 4 def play(name):
print(f"{name} play 1") # 5
print(current_thread().name) # 6
# gevent.sleep(1)
time.sleep(1)
print(f"{name} play 2") # 7 g1 = gevent.spawn(eat, "egon")
g2 = gevent.spawn(play, "egon")
print(f"主{current_thread().name}") # 1
g1.join()
g2.join()
结果:
主MainThread
egon eat 1
MainThread
egon eat 2
egon play 1
MainThread
egon play 2
最终版本
import gevent
import time
from gevent import monkey
monkey.patch_all() # 打补丁:将下面所有任务的阻塞都打上标记 def eat(name):
print(f"{name} eat 1") # 1
time.sleep(2)
print(f"{name} eat 2") # 4 def play(name):
print(f"{name} play 1") # 2
time.sleep(1)
print(f"{name} play 2") # 3 g1 = gevent.spawn(eat, "egon")
g2 = gevent.spawn(play, "egon") # g1.join()
# g2.join()
gevent.joinall([g1, g2])
结果:
egon eat 1
egon play 1
egon play 2
egon eat 2
协程的应用

爬虫

from gevent import monkey;monkey.patch_all()
import gevent
import requests
import time def get_page(url):
print('GET: %s' %url)
response=requests.get(url)
if response.status_code == 200:
print('%d bytes received from %s' %(len(response.text),url)) start_time=time.time()
gevent.joinall([
gevent.spawn(get_page,'https://www.python.org/'),
gevent.spawn(get_page,'https://www.yahoo.com/'),
gevent.spawn(get_page,'https://github.com/'),
])
stop_time=time.time()
print('run time is %s' %(stop_time-start_time)) 结果:
GET: https://www.python.org/
GET: https://www.yahoo.com/
GET: https://github.com/
48919 bytes received from https://www.python.org/
87845 bytes received from https://github.com/
515896 bytes received from https://www.yahoo.com/
run time is 2.729017734527588

通过gevent实现单线程下的socket并发(from gevent import monkey;monkey.patch_all()一定要放到导入socket模块之前,否则gevent无法识别socket的阻塞)

一个网络请求里面经过多个时间延迟time

server

from gevent import monkey;monkey.patch_all()
from socket import *
import gevent #如果不想用money.patch_all()打补丁,可以用gevent自带的socket
# from gevent import socket
# s=socket.socket() def server(server_ip,port):
s=socket(AF_INET,SOCK_STREAM)
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind((server_ip,port))
s.listen(5)
while True:
conn,addr=s.accept()
gevent.spawn(talk,conn,addr) def talk(conn,addr):
try:
while True:
res=conn.recv(1024)
print('client %s:%s msg: %s' %(addr[0],addr[1],res))
conn.send(res.upper())
except Exception as e:
print(e)
finally:
conn.close() if __name__ == '__main__':
server('127.0.0.1',8080)

client

from socket import *

client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080)) while True:
msg=input('>>: ').strip()
if not msg:continue client.send(msg.encode('utf-8'))
msg=client.recv(1024)

或多线程并发多个客户端,去请求上面的服务端是没问题的

from threading import Thread
from socket import *
import threading def client(server_ip,port):
c=socket(AF_INET,SOCK_STREAM) #套接字对象一定要加到函数内,即局部名称空间内,放在函数外则被所有线程共享,则大家公用一个套接字对象,那么客户端端口永远一样了
c.connect((server_ip,port)) count=0
while True:
c.send(('%s say hello %s' %(threading.current_thread().getName(),count)).encode('utf-8'))
msg=c.recv(1024)
print(msg.decode('utf-8'))
count+=1
if __name__ == '__main__':
for i in range(500):
t=Thread(target=client,args=('127.0.0.1',8080))
t.start()

协程的另外一个模块asyncio

#!/usr/bin/env python
# -*- coding:utf-8 -*- # import asyncio # 起一个任务.
# async def demo(): # 协程方法
# print('start')
# await asyncio.sleep(1) # 阻塞
# print('end') # loop = asyncio.get_event_loop() # 创建一个事件循环
# loop.run_until_complete(demo()) # 把demo任务丢到事件循环中去执行 # 启动多个任务,并且没有返回值
# async def demo(): # 协程方法
# print('start')
# await asyncio.sleep(1) # 阻塞
# print('end')
#
# loop = asyncio.get_event_loop() # 创建一个事件循环
# wait_obj = asyncio.wait([demo(),demo(),demo()])
# loop.run_until_complete(wait_obj) # 启动多个任务并且有返回值
# async def demo(): # 协程方法
# print('start')
# await asyncio.sleep(1) # 阻塞
# print('end')
# return 123
#
# loop = asyncio.get_event_loop()
# t1 = loop.create_task(demo())
# t2 = loop.create_task(demo())
# tasks = [t1,t2]
# wait_obj = asyncio.wait([t1,t2])
# loop.run_until_complete(wait_obj)
# for t in tasks:
# print(t.result()) # 谁先回来先取谁的结果
# import asyncio
# async def demo(i): # 协程方法
# print('start')
# await asyncio.sleep(10-i) # 阻塞
# print('end')
# return i,123
#
# async def main():
# task_l = []
# for i in range(10):
# task = asyncio.ensure_future(demo(i))
# task_l.append(task)
# for ret in asyncio.as_completed(task_l):
# res = await ret
# print(res)
#
# loop = asyncio.get_event_loop()
# loop.run_until_complete(main()) # import asyncio
#
# async def get_url():
# reader,writer = await asyncio.open_connection('www.baidu.com',80)
# writer.write(b'GET / HTTP/1.1\r\nHOST:www.baidu.com\r\nConnection:close\r\n\r\n')
# all_lines = []
# async for line in reader:
# data = line.decode()
# all_lines.append(data)
# html = '\n'.join(all_lines)
# return html
#
# async def main():
# tasks = []
# for url in range(20):
# tasks.append(asyncio.ensure_future(get_url()))
# for res in asyncio.as_completed(tasks):
# result = await res
# print(result)
#
#
# if __name__ == '__main__':
# loop = asyncio.get_event_loop()
# loop.run_until_complete(main()) # 处理一个任务 # python原生的底层的协程模块
# 爬虫 webserver框架
# 题高网络编程的效率和并发效果
# 语法
# await 阻塞 协程函数这里要切换出去,还能保证一会儿再切回来
# await 必须写在async函数里,async函数是协程函数
# loop 事件循环
# 所有的协程的执行 调度 都离不开这个loop

day38——线程queue、事件event、协程的更多相关文章

  1. 并发编程~~~多线程~~~线程queue, 事件event,

    一 线程queue 多线程抢占资源,只能让其串行. 互斥锁 队列 import queue q = queue.Queue() # 先进先出 q = queue.LifoQueue() # 先进后出 ...

  2. Python自动化运维之16、线程、进程、协程、queue队列

    一.线程 1.什么是线程 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位. 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行 ...

  3. Event事件与协程

    1.Event事件 Event事件的作用: - 用来控制线程的执行. - 由一些线程去控制另一些线程. 2.进程池与线程池 1)什么是进程池与线程池? 进程池与线程池是用来控制当前程序允许创建(进程/ ...

  4. python并发编程之Queue线程、进程、协程通信(五)

    单线程.多线程之间.进程之间.协程之间很多时候需要协同完成工作,这个时候它们需要进行通讯.或者说为了解耦,普遍采用Queue,生产消费模式. 系列文章 python并发编程之threading线程(一 ...

  5. Python之线程、进程和协程

    python之线程.进程和协程 目录: 引言 一.线程 1.1 普通的多线程 1.2 自定义线程类 1.3 线程锁 1.3.1 未使用锁 1.3.2 普通锁Lock和RLock 1.3.3 信号量(S ...

  6. python运维开发(十一)----线程、进程、协程

    内容目录: 线程 基本使用 线程锁 自定义线程池 进程 基本使用 进程锁 进程数据共享 进程池 协程 线程 线程使用的两种方式,一种为我们直接调用thread模块上的方法,另一种我们自定义方式 方式一 ...

  7. python之路: 线程、进程和协程

    进程和线程 既然看到这一章,那么你肯定知道现在的系统都是支持“多任务”的操作,比如: Mac OS X,UNIX,Linux,Windows等. 多任务:简单地说就是同时运行多个任务.譬如:你可以一边 ...

  8. python第四课——线程、进程、协程

    面试or笔试题:简述线程.进程.协程之间的关系? 内容概要 1.进程与线程优.缺点的比较 2.适用情况 3.线程 线程的创建 setDaemon join event RLock 队列 4.进程 创建 ...

  9. python基础-第九篇-9.1初了解Python线程、进程、协程

    了解相关概念之前,我们先来看一张图 进程: 优点:同时利用多个cpu,能够同时进行多个操作 缺点:耗费资源(重新开辟内存空间) 线程: 优点:共享内存,IO操作时候,创造并发操作 缺点:抢占资源 通过 ...

随机推荐

  1. ajax 样式

    Ajax 由 HTML.JavaScript™ 技术.DHTML 和 DOM 组成,这一杰出的方法可以将笨拙的 Web 界面转化成交互性的 Ajax 应用程序.它是一种构建网站的强大方法. 使用aja ...

  2. 关于System.ArgumentNullException异常

    什么是ArgumentNullException 当将 null 引用(Visual Basic 中为 Nothing)传递到不接受其作为有效参数的方法时引发的异常. 继承 Object Except ...

  3. 16-ESP8266 SDK开发基础入门篇--TCP 服务器 非RTOS运行版,串口透传(串口回调函数处理版)

    https://www.cnblogs.com/yangfengwu/p/11105466.html 其实官方给的RTOS的版本就是在原先非RTOS版本上增加的 https://www.cnblogs ...

  4. CSP内容安全策略总结及如何抵御 XSS 攻击

    跨域脚本攻击 XSS 是最常见.危害最大的网页安全漏洞.为了防止它们,要采取很多编程措施,非常麻烦.很多人提出,能不能根本上解决问题,浏览器自动禁止外部注入恶意脚本?这就是"网页安全政策&q ...

  5. Web前端开发(高级)下册-目录

    多媒体与绘图 多媒体音频格式视频格式 HTML5多媒体支持 <audio>和<video> htmlaudioElement和htmlVideoElement <audi ...

  6. 求斐波那契数列中的第N个数

    递推 递归 1.暴力递归 2.记忆化递归 对比下二者的效率

  7. 行业大秀:EasyEarth Show!

    EasyEarth三维可视化地理信息云平台是由北京四维益友信息技术有限公司自主研发的新一代面向三维可视化应用领域的基础信息系统平台. EasyEarth以数据管理为核心,围绕7大类基础数据,提供综合管 ...

  8. 第07组 Alpha冲刺(6/6)

    队名:摇光 队长:杨明哲 组长博客:求戳 作业博客:求再戳 队长:杨明哲 过去两天完成了哪些任务 文字/口头描述:博客生成的逻辑 展示GitHub当日代码/文档签入记录:(组内共用,已询问过助教小姐姐 ...

  9. 实验与作业(Python)-02 Python函数入门与温度转换程序(函数、input、eval、int、float、列表)

    截止日期 实验目标 学会定义函数,使用函数.学会导入在某个文件中定义的函数. input获得值,然后通过eval或者int.float将其转换为相应的类型. 学会使用列表:访问列表.append.遍历 ...

  10. jQuery跳出each循环:JS报错:illegal break statement

    今天在JS中运用jquery中each写一个简单的循环语句时,在执行跳出循环操作时,遇到JS报错:Uncaught SyntaxError: illegal break statement 非法的br ...