day38

线程queue

多线程抢占资源

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

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

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

事件event

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

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

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

  1. from threading import Thread
  2. from threading import current_thread
  3. from threading import Event
  4. import time
  5. event = Event()
  6. def check():
  7. print(f"{current_thread().name} 监测服务器是否开启")
  8. time.sleep(2)
  9. event.set()
  10. def connect():
  11. print(f"{current_thread().name} 等待连接,,,")
  12. for i in range(3):
  13. event.wait(1)
  14. if event.is_set():
  15. print("服务器已经开启")
  16. print(f"{current_thread().name} 连接成功")
  17. break
  18. else:
  19. print(f"{current_thread().name} 连接失败{i+1}次")
  20. t1 = Thread(target=check)
  21. t2 = Thread(target=connect)
  22. t1.start()
  23. 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模块。

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

爬虫

  1. from gevent import monkey;monkey.patch_all()
  2. import gevent
  3. import requests
  4. import time
  5. def get_page(url):
  6. print('GET: %s' %url)
  7. response=requests.get(url)
  8. if response.status_code == 200:
  9. print('%d bytes received from %s' %(len(response.text),url))
  10. start_time=time.time()
  11. gevent.joinall([
  12. gevent.spawn(get_page,'https://www.python.org/'),
  13. gevent.spawn(get_page,'https://www.yahoo.com/'),
  14. gevent.spawn(get_page,'https://github.com/'),
  15. ])
  16. stop_time=time.time()
  17. print('run time is %s' %(stop_time-start_time))
  18. 结果:
  19. GET: https://www.python.org/
  20. GET: https://www.yahoo.com/
  21. GET: https://github.com/
  22. 48919 bytes received from https://www.python.org/
  23. 87845 bytes received from https://github.com/
  24. 515896 bytes received from https://www.yahoo.com/
  25. run time is 2.729017734527588

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

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

server

  1. from gevent import monkey;monkey.patch_all()
  2. from socket import *
  3. import gevent
  4. #如果不想用money.patch_all()打补丁,可以用gevent自带的socket
  5. # from gevent import socket
  6. # s=socket.socket()
  7. def server(server_ip,port):
  8. s=socket(AF_INET,SOCK_STREAM)
  9. s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
  10. s.bind((server_ip,port))
  11. s.listen(5)
  12. while True:
  13. conn,addr=s.accept()
  14. gevent.spawn(talk,conn,addr)
  15. def talk(conn,addr):
  16. try:
  17. while True:
  18. res=conn.recv(1024)
  19. print('client %s:%s msg: %s' %(addr[0],addr[1],res))
  20. conn.send(res.upper())
  21. except Exception as e:
  22. print(e)
  23. finally:
  24. conn.close()
  25. if __name__ == '__main__':
  26. server('127.0.0.1',8080)

client

  1. from socket import *
  2. client=socket(AF_INET,SOCK_STREAM)
  3. client.connect(('127.0.0.1',8080))
  4. while True:
  5. msg=input('>>: ').strip()
  6. if not msg:continue
  7. client.send(msg.encode('utf-8'))
  8. msg=client.recv(1024)

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

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

协程的另外一个模块asyncio

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. # import asyncio
  4. # 起一个任务.
  5. # async def demo(): # 协程方法
  6. # print('start')
  7. # await asyncio.sleep(1) # 阻塞
  8. # print('end')
  9. # loop = asyncio.get_event_loop() # 创建一个事件循环
  10. # loop.run_until_complete(demo()) # 把demo任务丢到事件循环中去执行
  11. # 启动多个任务,并且没有返回值
  12. # async def demo(): # 协程方法
  13. # print('start')
  14. # await asyncio.sleep(1) # 阻塞
  15. # print('end')
  16. #
  17. # loop = asyncio.get_event_loop() # 创建一个事件循环
  18. # wait_obj = asyncio.wait([demo(),demo(),demo()])
  19. # loop.run_until_complete(wait_obj)
  20. # 启动多个任务并且有返回值
  21. # async def demo(): # 协程方法
  22. # print('start')
  23. # await asyncio.sleep(1) # 阻塞
  24. # print('end')
  25. # return 123
  26. #
  27. # loop = asyncio.get_event_loop()
  28. # t1 = loop.create_task(demo())
  29. # t2 = loop.create_task(demo())
  30. # tasks = [t1,t2]
  31. # wait_obj = asyncio.wait([t1,t2])
  32. # loop.run_until_complete(wait_obj)
  33. # for t in tasks:
  34. # print(t.result())
  35. # 谁先回来先取谁的结果
  36. # import asyncio
  37. # async def demo(i): # 协程方法
  38. # print('start')
  39. # await asyncio.sleep(10-i) # 阻塞
  40. # print('end')
  41. # return i,123
  42. #
  43. # async def main():
  44. # task_l = []
  45. # for i in range(10):
  46. # task = asyncio.ensure_future(demo(i))
  47. # task_l.append(task)
  48. # for ret in asyncio.as_completed(task_l):
  49. # res = await ret
  50. # print(res)
  51. #
  52. # loop = asyncio.get_event_loop()
  53. # loop.run_until_complete(main())
  54. # import asyncio
  55. #
  56. # async def get_url():
  57. # reader,writer = await asyncio.open_connection('www.baidu.com',80)
  58. # writer.write(b'GET / HTTP/1.1\r\nHOST:www.baidu.com\r\nConnection:close\r\n\r\n')
  59. # all_lines = []
  60. # async for line in reader:
  61. # data = line.decode()
  62. # all_lines.append(data)
  63. # html = '\n'.join(all_lines)
  64. # return html
  65. #
  66. # async def main():
  67. # tasks = []
  68. # for url in range(20):
  69. # tasks.append(asyncio.ensure_future(get_url()))
  70. # for res in asyncio.as_completed(tasks):
  71. # result = await res
  72. # print(result)
  73. #
  74. #
  75. # if __name__ == '__main__':
  76. # loop = asyncio.get_event_loop()
  77. # loop.run_until_complete(main()) # 处理一个任务
  78. # python原生的底层的协程模块
  79. # 爬虫 webserver框架
  80. # 题高网络编程的效率和并发效果
  81. # 语法
  82. # await 阻塞 协程函数这里要切换出去,还能保证一会儿再切回来
  83. # await 必须写在async函数里,async函数是协程函数
  84. # loop 事件循环
  85. # 所有的协程的执行 调度 都离不开这个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. Linux 重启 PHP-FPM 命令

    1. 停止命令 pkill php-fpm 2.重启或启动命令 php-fpm -R

  2. 75: libreoj #10028 双向宽搜

    $des$ 实现一个bfs $sol$ 写了一个双向bfs #include <bits/stdc++.h> using namespace std; #define Rep(i, a, ...

  3. THUPC&CTS 2019 游记

    day ? 去THU报了个到. day? THUPC比赛日,三个人都没有智商,各种签到题不会做,被各路神仙吊着打.G题还猜了个假结论,做了好久都不对.最后顺利打铁了. 还顺便去看一下THUAC. da ...

  4. 2017.10.2 国庆清北 D2T1 (a*b)|x

    在电脑上后面仨点过不了,要用I64d,lld会炸.但是洛谷上要用lld,LINUX系统没有I64d /* 求一个数对满足 (a*b)|n,也就是求三个数 a*b*c=n,那么求1~n之间的,就是a*b ...

  5. 洛谷P1081 开车旅行

    题目 双向链表+倍增+模拟. \(70pts\): 说白了此题的暴力就是细节较多的模拟题. 我们设离\(i\)城市最近的点的位置为\(B[i]\),第二近的位置为\(A[i]\).设\(A\)或\(B ...

  6. mysql 字段拼接

    mysql> select concat(name,"**",id) as test from test; +----------------+ | test | +---- ...

  7. lintcode-1174.下一个更大的元素 III

    题目描述: 1174. 下一个更大的元素 III 给定一个32位整数n,用同样的数字组成新的32位整数,使得它要比n大,返回最小的这样的数.如果不存在这样的整数,返回-1. 算法思路: 首先将这个数转 ...

  8. 在安卓手机下按钮会悬浮在键盘上,怎么解决vue.js

    data里面 screenHeight: window.innerHeight mounted里面 mounted () { var that = this var u = navigator.use ...

  9. 拉格朗日插值法(c++)

    已给sin0.32=0.314567,sin0.34=0.333487,sin0.36=0.352274,计算sin0.3367的值 #include <iostream> #includ ...

  10. CTF 文件上传

    目录 一.客户端校验 1.禁用JS 2.抓包改包 二.服务端校验 1.MIME类型检测 2.后缀名黑名单校验 3.后缀名白名单校验 4.内容头校验 5.竞争上传 6.过滤<?或php 两种校验方 ...