day 35 关于线程
并发编程之协程
对于单线程下,我们不可避免程序中出现io操作,但如果我们能在自己的程序中(即用户程序级别,而非操作系统级别)控制单线程下的多个任务能在一个任务遇到io阻塞时就切换到另外一个任务去计算,这样就保证了该线程能够最大限度地处于就绪态,即随时都可以被cpu执行的状态,相当于我们在用户程序级别将自己的io操作最大限度地隐藏起来,从而可以迷惑操作系统,让其看到:该线程好像是一直在计算,io比较少,从而更多的将cpu的执行权限分配给我们的线程。
协程的本质就是在单线程下,由用户自己控制一个任务遇到io阻塞了就切换另外一个任务去执行,以此来提升效率。为了实现它,我们需要找寻一种可以同时满足以下条件的解决方案:可以控制多个任务之间的切换,切换之前将任务的状态保存下来,以便重新运行时,可以基于暂停的位置继续执行;可以检测io操作,在遇到io操作的情况下才发生切换。
一、协程介绍
1、定义
协程是单线程下的并发,又称微线程。协程是一种用户态的轻量级的线程,即协程是由用户程序自己控制调度的。强调如下:
(1)python的线程属于内核级别的,即由操作系统控制调度,如果遇到io或者执行时间过长,操作系统就会强迫要求交出cpu的执行权限,执行其他线程;
(2)单线程内开启协程,一旦遇到io就会从应用程序的级别进行切换,从而提升效率。注意:一定是遇到IO才切
greenlet模块
2、优点
优点如下:
(1)协程的切换的开销较小,属于程序级别的切换,操作系统无感知,因而更加的轻量级;
(2)实现单线程内的并发,最大限度的利用cpu。
3、缺点
缺点如下:
(1)协程的本质是在单线程下,无法利用多核;
(2)协程指的是单个线程,一旦协程遇到阻塞,则会阻塞整个线程。
4、总结协程特点:
- 必须在只有一个单线程里实现并发
- 修改共享数据不需加锁
- 用户程序里自己保存多个控制流的上下文栈
- 附加:一个协程遇到IO操作自动切换到其它协程(如何实现检测IO,yield、greenlet都无法实现,就用到了gevent模块(select机制))
二、greenlet模块
greenlet模块提供一种在多种任务之间来回切换的功能,这种切换手段非常便捷。如下例:
from greenlet import greenlet
def eat(name):
print('%s is eating1' %name)
g2.switch('alex') #2.切换执行play('alex')
print('%s is eating2' %name)
g2.switch() #4.切换继续执行play('alex')
def play(name):
print('%s is playing1' %name)
g1.switch() #3.切换继续执行eat('alex')
print('%s is playing2' %name)
g1=greenlet(eat)
g2=greenlet(play)
'''switch只有第一次切换时需要传参'''
g1.switch('alex') #1.切换执行eat('alex') '''
输出结果:
alex is eating1
alex is playing1
alex is eating2
alex is playing2
'''
![](https://common.cnblogs.com/images/copycode.gif)
以上实例中gevent.sleep()模拟gevent可以识别的IO,对于实际的应用没有意义。对于time.sleep()等gevent不可识别的IO的阻塞该如何解决呢?请看下例。
2、gevent不可识别的IO阻塞情况
实例:
![](https://common.cnblogs.com/images/copycode.gif)
from gevent import monkey;monkey.patch_all()
'''from gevent import monkey;monkey.patch_all()必须放在文件的最开头'''
import time,gevent
from threading import current_thread
def eat(name):
print('%s is running1:%s' %(name,current_thread().getName()))
time.sleep(2)
print('%s is running2:%s' %(name,current_thread().getName())) def play(name):
print('%s is playing1:%s' %(name,current_thread().getName()))
time.sleep(1)
print('%s is playing2:%s' % (name, current_thread().getName())) start=time.time()
g1=gevent.spawn(eat,'alex')
g2=gevent.spawn(play,name='alex')
gevent.joinall([g1,g2])
stop=time.time()
print(stop-start)
'''
alex is running1:DummyThread-1
alex is playing1:DummyThread-2
alex is playing2:DummyThread-2
alex is running2:DummyThread-1
2.0029842853546143
'''
![](https://common.cnblogs.com/images/copycode.gif)
上例才是我们最终实现的协程,为解决识别正常阻塞的问题,我们采用打补丁的方式:from gevent import monkey;monkey.patch_all(),补丁必须放在文件的最开头。结果中
DummyThread-n翻译为假线程的意思,并不是开启了多线程。
四、协程实例
1、协程实现爬虫实例
![](https://common.cnblogs.com/images/copycode.gif)
from gevent import monkey
monkey.patch_all()
import gevent
import requests
from threading import current_thread
def get(url):
print('%s get %s' %(current_thread().getName(),url))
response=requests.get(url) #存在IO
if response.status_code==2:
return {'url': len(response.text)} g1=gevent.spawn(get,'www.baidu.com')
g2=gevent.spawn(get,'www.qq.com')
g3=gevent.spawn(get,'www.jd.com') g1.join()
g2.join()
g3.join() print(g1.value) #value为取值
print(g2.value)
print(g3.value)
![](https://common.cnblogs.com/images/copycode.gif)
2、协程实现socket通信
服务端:
![](https://common.cnblogs.com/images/copycode.gif)
from gevent import monkey
monkey.patch_all()
import gevent
from socket import *
from threading import current_thread sever = socket(AF_INET,SOCK_STREAM) # 默认参数可以不传
sever.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
sever.bind(('127.0.0.1',8090))
sever.listen()
def talk(conn,addr):
print('%s got a connect from %s:%s'%(current_thread().getName(),addr[0],addr[1]))
while True:
data = conn.recv(1024).decode('utf-8')
if not data:break
conn.send(data.upper().encode()) if __name__ == '__main__':
while True:
conn,addr = sever.accept()
g = gevent.spawn(talk,conn,addr)
![](https://common.cnblogs.com/images/copycode.gif)
客户端
![](https://common.cnblogs.com/images/copycode.gif)
from socket import *
client=socket()
client.connect(('127.0.0.1',8090))
while True:
msg=input('>>>').strip()
if not msg:continue
client.send(msg.encode())
res=client.recv(1024).decode()
print(res) #多线程开客户端
# from threading import Thread
# from socket import *
# client=socket()
# client.connect(('127.0.0.1',8090))
# def talk(client):
# client.send('hello'.encode())
# res=client.recv(1024).decode()
# print(res)
#
# if __name__ == '__main__':
# for i in range(100):
# t=Thread(target=talk,args=(client,))
# t.start()
day 35 关于线程的更多相关文章
- 35.QT-多线程
程序和进程的区别 进程是动态的,程序是静态的,进程是程序运行时的实例,是占用系统运行资源的程序 进程是暂时的,程序是永久的, 进程是通过程序运行时得到的 程序是一个数据文件,进程是内存中动态的运行实体 ...
- SpringCloud升级之路2020.0.x版-35. 验证线程隔离正确性
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 上一节我们通过单元测试验证了重试的正确性,这一节我们来验证我们线程隔离的正确性,主要包括: ...
- Java线程(学习整理)--1--守护线程
1.什么是守护线程? 今天老师讲解我才知道有守护线程这回事!原来守护线程经常存在于我们的身边,比如:一个免费的网页游戏,里面都会或多或少有些插入性的广告!! 一般情况下,我们不会去点击这些广告的,但是 ...
- tomcat线程初探
博主:handsomecui,希望路过的各位大佬留下你们宝贵的意见,在这里祝大家冬至快乐. 缘由: 初探缘由,在业务层想要通过(当前线程的栈)来获取到控制层的类名,然后打日志,可是发现并不能通过当前线 ...
- Tomcat中常见线程说明
http://blog.csdn.NET/jeff_fangji/article/details/41786205 本文讲述了Tomcat的常见线程的功能.名称.线程池和配置等信息,其中源码来自于To ...
- java多线程系列六、线程池
一. 线程池简介 1. 线程池的概念: 线程池就是首先创建一些线程,它们的集合称为线程池. 2. 使用线程池的好处 a) 降低资源的消耗.使用线程池不用频繁的创建线程和销毁线程 b) 提高响应速度,任 ...
- Windows驱动开发之线程与同步事件
转载请注明来源: enjoy5512的博客 : http://blog.csdn.net/enjoy5512 GitHub : https://github.com/whu-enjoy .1. 使用系 ...
- 2018 Java线程热门面试题,你知道多少?
面试,难还是不难?取决于面试者的底蕴(气场+技能).心态和认知及沟通技巧.面试其实可以理解为一场聊天和谈判,在这过程中有心理.思想上的碰撞和博弈.其实你只需要搞清楚一个逻辑:“面试官为什么会这样问?他 ...
- 多线程系列 - 基础篇01 - 线程基本概念 & 线程优先级 & 守护线程 60%
1.什么是线程 将线程理解为轻量级进程,它与进程的最大的区别是: 多个线程共享一个进程资源: 对于OS的许多资源的分配和管理(如内存)通常都是进程级别的,线程只是os调度的最小单位: 相对于进程来说更 ...
随机推荐
- 模拟curl函数
只要需要调用微信的网址,就需要模拟curl请求 $tokenUrl="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_cr ...
- Mysql中从一张表中的数据添加到另一张表
A为原表 B为要加入的表$sql="insert into B select * from A where id=$id";
- xml的解构与组装
xml的结构 <xml> <ToUserName><![CDATA[%s]]></ToUserName> <FromUserName>< ...
- git 更新代码到本地
正规流程 git status(查看本地分支文件信息,确保更新时不产生冲突) git checkout – [file name] (若文件有修改,可以还原到最初状态; 若文件需要更新到服务器上,应该 ...
- spring boot(十八)集成FastDFS文件上传下载
上篇文章介绍了如何使用Spring Boot上传文件,这篇文章我们介绍如何使用Spring Boot将文件上传到分布式文件系统FastDFS中. 这个项目会在上一个项目的基础上进行构建. 1.pom包 ...
- Matlab-1:jacobi迭代法工具箱
function [u,n]=Jacobi(A,b,u0,eps,varargin) %Jacobi.m函数为用于雅可比迭代法求解线性方程组 %A为线性方程组的系数矩阵 %b为线性方程组的常数向量 % ...
- c++ shared_ptr的使用
shared_ptr.是c++为了提高指针安全性而添加的智能指针,方便了内存管理.功能非常强大,非常强大,非常强大(不单单是shared_ptr,配合week_ptr以及enable_share_fr ...
- SEND EMAIL SO_DOCUMENT_SEND_API1
FUNCTION zcrm_send_email_and_attach . *"------------------------------------------------------- ...
- free结果解释
free用于查询内存使用情况,不过其多个数值经常让人感到迷惑,下边对其结果各值进行解释. Mem 1862--总内存/1770--已使用内存/92--未使用内存/199--输出缓存/637--输入缓存 ...
- js--阻止冒泡,捕获,默认行为
防止冒泡和捕获 w3c的方法是e.stopPropagation(),IE则是使用e.cancelBubble = true· var el = window.document.getElementB ...