内容回顾

  1. 11:30
  2. 码云 :王老师检查作业+定期抽查
    • 注册账号
  3. 考试的时间

threading.enumerate(),能够获取到当前正在运行的所有线程对象列表

守护线程

  • 守护线程会等待所有的非守护线程结束之后结束
  • 守护线程的结束是因为主进程的结束
  • 在start之前设置daemon = True
    ####锁
  • GIL锁 : 全局解释器锁,在Cpython解释器中,同一时刻同一个进程内只能有一个线程访问CPU
  • 互斥锁
  • 锁的是代码,一段代码被多个线程执行,并且要对全局的变量进行非原子性操作
  • 互斥锁 : 在同一个线程中,不能连续acquire多次,并且可以做到多个线程中被锁的代码同时只有一个线程执行
  • 递归锁 : 在同一个线程中,能连续acquire多次,并且可以做到多个线程中被锁的代码同时只有一个线程执行
    * 从一定程度上可以避免死锁现象
    * 使用递归锁也会产生死锁现象
  • 死锁现象 : 只要实例化多把锁,并交替使用,都有可能产生死锁现象
    * 只要是1把锁,递归锁永远不死锁
    * 只要是2以及以上,交替使用,
    * 只要是2以及以上,交替使用,递归锁互斥锁都可能死锁
    ####队列
  • from queue import Queue,LifoQueue,PriorityQueue
  • queue这个模块提供的所有队列都是线程安全的
  • 先进先出 Queue
  • 后进先出 LifoQueue
  • 优先级队列 PriorityQueue
    ####进程池
  • call_back回调函数
concurrent.futures
ProcessPoolExecutor
from concurrent.futures import ProcessPoolExecutor
pool = ProcessPoolExecutor(5)
pool进程池对象
ret = pool.map(func,iter)
#ret是一个可迭代对象,迭代器(惰性运算),可以直接通过for循环从ret中获取返回值

submit

  • 1. for + submit(func,arg1,arg2) = map
  • 任务对象task
  • 2. task = submit(func)
  • 3. task.result() 获取任务函数的返回值 (阻塞方法)
  • 4. task.add_done_callback(回调函数)
    ####shutdown
  • pool.shutdown() 等待进程池中所有的任务都执行完毕之后结束阻塞
import time
from concurrent.futures import ProcessPoolExecutor

def func():
    time.sleep(1)
    return '*'*20

if __name__ == '__main__':
    p = ProcessPoolExecutor(4)
    task = p.submit(func)
    time.sleep(2)
    print('---->')

    print(task.result())   * 阻塞方法

  • time/random 在任务中睡一会儿,在主线程可以睡一会儿
  • threading.current_thread().ident

线程池

from urllib.request import urlopen
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor

def get_html(name,addr):
   ret = urlopen(addr)
   return {'name':name,'content':ret.read()}

def parser_page(ret_obj):
   dic = ret_obj.result()
   with open(dic['name']+'.html','wb') as f:
       f.write(dic['content'])

url_lst = {
   '协程':'http://www.cnblogs.com/Eva-J/articles/8324673.html',
   '线程':'http://www.cnblogs.com/Eva-J/articles/8306047.html',
   '目录':'https://www.cnblogs.com/Eva-J/p/7277026.html',
   '百度':'http://www.baidu.com',
   'sogou':'http://www.sogou.com'
}

t = ThreadPoolExecutor(20)
for url in url_lst:
   task = t.submit(get_html,url,url_lst[url])
   task.add_done_callback(parser_page)

task.result()
t.map()
t.shutdown()

使用多线程去执行get_html获取网页对应的内容

一旦get_html执行结束之后,立即使用parser_page来分析获取的页面结果

进程池 除非高计算型的场景否则几乎不用 CPU的个数*2 线程池 cpu的个数*5

4 cpu 4*20 = 80

协程

  • 进程 计算机中最小的资源分配单位
  • 线程 计算机中被CPU调度的最小单位

操作系统 负责调度 线程

  • 对于操作系统来说 可见的最小单位就是线程
  • 线程的开销比进程虽然小很多,但是开启线程\关闭线程仍然需要开销

协程(本质是一条线程,操作系统不可见)

  • 是有程序员操作的,而不是由操作系统调度的
  • 多个协程的本质是一条线程,所以多个协程不能利用多核
  • 出现的意义 : 多个任务中的IO时间可以共享,当执行一个任务遇到IO操作的时候,

    • 可以将程序切换到另一个任务中继续执行
    • 在有限的线程中,实现任务的并发,节省了调用操作系统创建\销毁线程的时间
    • 并且协程的切换效率比线程的切换效率要高
    • 协程执行多个任务能够让线程少陷入阻塞,让线程看起来很忙
    • 线程陷入阻塞的次数越少,那么能够抢占CPU资源就越多,你的程序效率看起来就越高

    • 1.开销变小了

    • 2.效率变高了

两个任务,一个任务执行过程中遇到io操作就切到另一个任务执行

直到所有的任务都遇到IO操作了,才进入阻塞,其中有任何一个任务结束阻塞,都会回到就绪队列里

协程的本质 - 就是在多个任务之间能够来回切换

做切换

def consumer():
    n = yield
    n = yield
    n = yield
    n = yield
    n = yield
    n = yield
    n = yield
    n = yield

def producer():
    g = consumer()
    next(g)
    for i in range(2000000):
        g.send(i)

tornado python的web的异步框架 yield --> asyncio

协程的切换 会不会占用时间

  • 仍然是占用时间的
  • 基本在函数调用的时间级别
  • 不依赖操作系统

扩展模块\第三方模块

  • python语言 写了一段代码\一个功能
  • c语言 写了一段代码\编译之后作为一个python模块为你提供功能

yield这个切换的功能是C语言能写的

能不能用C语言写一个第三方模块也能实现yield的效果

协程模块 帮助我们更加简单的进行函数之间的切换

import time
from greenlet import greenlet   * 协程模块
def eat():    * 协程任务 协程函数
    print('start eating')
    g2.switch()
    time.sleep(1)
    print('end eating')
    g2.switch()

def sleep():  * 协程任务 协程函数
    print('start sleeping')
    g1.switch()
    time.sleep(1)
    print('end sleeping')

g1 = greenlet(eat)
g2 = greenlet(sleep)
g1.switch()

gevent模块

import time
import gevent

def eat():    * 协程任务 协程函数
    print('start eating')
    gevent.sleep(1)
    print('end eating')

def sleep():  * 协程任务 协程函数
    print('start sleeping')
    gevent.sleep(1)
    print('end sleeping')

g1 = gevent.spawn(eat)
g2 = gevent.spawn(sleep)
* g1.join()   * 阻塞,直到g1任务执行完毕
* g2.join()   * 阻塞,直到g2任务执行完毕
gevent.joinall([g1,g2])

from gevent import monkey
monkey.patch_all()
import time
import gevent

def eat():    * 协程任务 协程函数
    print('start eating')
    time.sleep(1)
    print('end eating')

def sleep():  * 协程任务 协程函数
    print('start sleeping')
    time.sleep(1)
    print('end sleeping')

g1 = gevent.spawn(eat)   * 创建协程
g2 = gevent.spawn(sleep)
gevent.joinall([g1,g2])  * 阻塞 直到协程任务结束

请求网页
url_dic = {
   '协程':'http://www.cnblogs.com/Eva-J/articles/8324673.html',
   '线程':'http://www.cnblogs.com/Eva-J/articles/8306047.html',
   '目录':'https://www.cnblogs.com/Eva-J/p/7277026.html',
   '百度':'http://www.baidu.com',
   'sogou':'http://www.sogou.com',
   '4399':'http://www.4399.com',
   '豆瓣':'http://www.douban.com',
   'sina':'http://www.sina.com.cn',
   '淘宝':'http://www.taobao.com',
   'JD':'http://www.JD.com'
}

import time
from gevent import monkey;monkey.patch_all()
from urllib.request import urlopen
import gevent

def get_html(name,url):
   ret = urlopen(url)
   content = ret.read()
   with open(name,'wb') as f:
       f.write(content)

start = time.time()
for name in url_dic:
   get_html(name+'_sync.html',url_dic[name])
ret = time.time() - start
print('同步时间 :',ret)

start = time.time()
g_l = []
for name in url_dic:
   g = gevent.spawn(get_html,name+'_async.html',url_dic[name])
   g_l.append(g)
gevent.joinall(g_l)
ret = time.time() - start
print('异步时间 :',ret)

高并发socket

import socket
import gevent
from gevent import monkey

monkey.patch_all()

sk = socket.socket()
sk.bind(('127.0.0.1',9001))
sk.listen()

def talk(conn):
   while True:
       msg = conn.recv(1024).decode('utf-8')
       conn.send(msg.upper().encode('utf-8'))

while True:
   conn,_ = sk.accept()
   gevent.spawn(talk,conn)

一条线程抗500个并发

进程 高计算型的场景下
线程 对于IO操作的检测是更加全面且灵敏的
协程 能够检测到的io操作是有限的

#20 * 500 = 10000
#5个进程 * 20 * 500 = 50000

Client

import socket
from threading import Thread
def client(i):
    sk = socket.socket()
    sk.connect(('127.0.0.1',9001))
    while True:
        sk.send(b'hello')
        print(sk.recv(1024))

for  i in range(500):
    Thread(target=client,args=(i,)).start()

ClientII

import socket

sk = socket.socket()
sk.connect(('127.0.0.1',9001))
while True:
    sk.send(b'beybey')
    print(sk.recv(1024))

2019-04-19-day036-协程与进程池的更多相关文章

  1. python--再看并行之协程线程进程

    1.gevent协程适合I/O密集,不适合CPU密集. 3.gevent协程无法发挥多核优势,事实上,协程只是以单线程的方式在运行. 3.子程序就是协程的一种特例 项目实际应用 from gevent ...

  2. 11.python之线程,协程,进程,

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

  3. 20170702-变量说明,静态方法,类方法区别,断点调试,fork,yield协程,进程,动态添加属性等。。

    概念: 并行:同时运行 并发:看似同时运行  json后任然中文的问题 import json d = {"名字":"初恋这件小事"} new_d1 = jso ...

  4. Python笔记_第四篇_高阶编程_进程、线程、协程_1.进程

    1. 多任务原理: 现代操作系统,像win,max os x,linux,unix等都支持多任务. * 什么叫做多任务? 操作系统可以同时运行多个任务. * 单核CPU实现多任务原理? 操作系统轮流让 ...

  5. 用Swoole4 打造高并发的PHP协程Mysql连接池

    码云代码仓库:https://gitee.com/tanjiajun/MysqlPool 代码仓库:https://github.com/asbectJ/swoole4.git 前言 在写这篇文章之前 ...

  6. Swoole2.0协程客户端连接池的实现

    Swoole2.0官方默认的实例是短连接的,在请求处理完毕后就会切断redis或mysql的连接.实际项目可以使用连接池实现复用. 实现原理也很简单,使用SplQueue,在请求到来时判断资源队列中是 ...

  7. Python笔记_第四篇_高阶编程_进程、线程、协程_3.进程vs线程

    1.多任务的实现原理: 通常我们会设计Mater-Workder模式,Master负责分配任务,Worker负责执行任务,因此多任务环境下,通常是一个Master,多个Worker 2.多进程: 主进 ...

  8. 2019.04.19 读书笔记 比较File.OpenRead()和File.ReadAllBytes()的差异

    最近涉及到流的获取与转化,终于要还流的债了. 百度了一下,看到这样的两条回复,于是好奇心,决定看看两种写法的源码差异. 先来看看OpenRead() public static FileStream ...

  9. 进程、线程、轻量级进程、协程与 go 的 goroutine【转载+整理】

    本文内容 进程 线程 协程 Go 中的 goroutine 参考资料 最近,看一些文章,提到"协程"的概念,心想,进程,线程,协程,前两个很容易,任何一本关于操作系统的书都有说,开 ...

随机推荐

  1. 20190321xlVBA_汇总表按模板生成明细表

    Public Sub 汇总表转信息表() '日期 '作者 Next 'QQ 84857038 Dim Wb, Sht, msht, NewSht, rng Set Wb = Application.T ...

  2. 登录获取token,token参数关联至所有请求的请求体内

    问题描述: 有些系统接口判断用户是否登录,是校验登录接口成功后传的token值,也就是请求系统所有接口时,前端传参必带登录成功后接口返回的token,后台以此检验是否过期或是否有登录.所有接口都依赖登 ...

  3. 前后端token机制 识别用户登录信息

    Token,就是令牌,最大的特点就是随机性,不可预测.一般黑客或软件无法猜测出来. 那么,Token有什么作用?又是什么原理呢? Token一般用在两个地方: 1)防止表单重复提交. 2)anti c ...

  4. restore和recover的区别

    restore 是还原物理文件 recover 是用日志恢复到一致 用了RMAN备份后就必须要用restore还原,然后才用recover恢复 restore——还原,与backup相对,从备份读出恢 ...

  5. sql数据表中的值重新命名

    select u.id,u.name,u.sex, 2 (case u.sex 3 when 1 then '男' 4 when 2 then '女' 5 else '空的' 6 end 7 )性别 ...

  6. mac下idea搭建maven项目的一些问题总结

    1.关于版本 目标原版与原发行版本不一致 <build> <finalName>comment</finalName> <plugins> <pl ...

  7. 嵌入式Linux开发之uboot启动Linux整体流程分析

    嵌入式Linux开发之uboot启动Linux整体流程分析 Uboot全称Universal Boot Loader,一个遵循GPL协议的的开源项目,其作用是引导操作系统,支持引导linux.VxWo ...

  8. Netty(一)——Netty入门程序

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7447618.html 有兴趣的可先了解下:4种I/O的对比与选型 主要内容包括: Netty开发环境的搭建 ...

  9. 网络传输数据序列化工具Protostuff

    一直在物色比较好用的网络传输数据序列化工具,看了诸如marshalling,protobuff等,但是均有一个共同特点,使用起来异常繁杂,有没有比较好用同时性能又不会太差的组件呢?答案当然是有的,那就 ...

  10. Mdate时间插件

    在做移动端的页面时,用户报名某个活动,需要填写她的出生日期,这时可以用Mdate插件来完成,已达到更好的用户体验 操作很简单,效果也不错,是滑动选择时间的,也有回调函数方便我们使用.只需要在页面中引入 ...