pythonsetblocking
⑴ 如何用python写一个协程
作者:LittleCoder
链接:https://www.hu.com/question/54483694/answer/139785021
来源:知乎
着作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
yield`和`yield from`的区别
`yield`题主肯定不陌生,而`yield from`是PEP 380中新增的一个特性。
PEP 380的名字是嵌套子迭代器的语法糖(我喜欢这么翻译,原文是:Syntax for Delegating to a Subgenerator)。
既然是语法糖,那么肯定本来是有别的写法的,这里给出本来的写法:
def subgen():
for i in range(3):
yield 'subgen: %s' % i
return 'subgen returned'def gen():
r = yield from subgen()
print('r = "%s"' % r)
yield rdef gen_without_yield_from():
sg = subgen()
try:
while 1:
yield sg.send(None)
except StopIteration as e:
yield e.valueprint('* [gen] get all values')for v in gen_without_yield_from():
print('get value: %s' % v)print('* [gen_without_yield_from] get all values')for v in gen_without_yield_from():
print('get value: %s' % v)
不难看出,`yield`子迭代器是把子迭代器直接传递出去,`yield from`子迭代器是把子迭代器的值一个一个传出去。
虽然实际把子迭代器当做一个对象直接传递出去也没有问题,也有使用场景(生成迭代器的迭代器)。
但在协程中相较于这个令人愉快的语法糖而言,直接传递就显得没有必要且碍事了。
毕竟我希望使用一个子迭代器是把子迭代器中的代码都运行一遍而不是直接就把这个子迭代器传出来让我自己操作。
所以如果你把子迭代器直接传了出去,asyncio就判断你在做一件奇怪的事情并报了错。
那么,回到问题,给出的程序要怎么通过`yield`调用呢?
# 源程序@asyncio.coroutinedef hello():
print("Hello world!")
yield from asyncio.sleep(1)
print("Hello again!")# 使用[email protected] hello():
print("Hello world!")
for v in asyncio.sleep(1):
yield v
print("Hello again!")
协程和迭代器的区别
举个比喻,迭代器和协程就像火药和枪械,利用火药的特性辅助各种其他东西才造出了枪械。
迭代器就最简单的本质而言就是一个可以暂停的程序。
那么就有这样一个合理的联想,我是不是可以节省下所有不必要的例如等待网站响应的等待时间。
就是我把我的请求发过去以后就把这个程序暂停下来,开启别的程序,等到响应来了再叫我回到这个程序。
那么等待网站响应的时间也就完全没有浪费了,比原来傻傻的等着网站响应真是优秀了许多。
这就是协程。
所以,为什么看上去都是`generator`,迭代器不会天生成为协程呢?
因为没有一个知道什么时候应该叫你回到这个程序的人。
这个人就是`event_loop`(消息循环)。
回到问题,协程是否可以脱离`event_loop`(消息循环)调用。
讲道理是不可以的,但合理联想一下是不是一直不停的告诉程序又到你了就行了。
像这样:
@asyncio.coroutinedef gen():
for i in range(3):
yield ifor i in gen():
print(i)print('end')
的确有些协程这样是可以运行的(这些协程为什么要写成协程?)。
但终究你是在不应该告诉程序到你的时候告诉了他这件事情。
所以显然获取数据的话当时数据根本没有传到,`sleep`的话就根本没有了`sleep`的效果。
只是看上去能够运行,实际完全没有用。
asyncio还为此特地加了一个断言,如果你这样调用`asyncio.sleep`,asyncio会发现你在伪装消息循环骗他。
协程的原理
这是另一个看上去能够运行,实际上完全没有用的事情。
这虽然不是你想问的问题,但你已经碰到了也迟早会意识到,所以一并讲了。
这个问题应该是这样的:为什么我写出来的协程完全没有协程的效果?
import time, [email protected] sleep(symbol, i):
time.sleep(i)
print('[%s] finished')loop = asyncio.get_event_loop()tasks = [sleep('A', 2), sleep('B', 2)]loop.run_until_complete(asyncio.wait(tasks))loop.close()
看到这里你起码可以简单的讲出来,因为显然我们在傻傻的等。
我们没有在开始等待的时候把程序暂停下来,然后在等待结束后继续运行程序,我们一心一意的在等。
我们真的`time.sleep`了两秒,而不是去做了两秒其他的事情。
你有各种选择,可以花式等待。我这里给你两个最基本的例子:
* get请求
* 同步变为协程(线程池)
get请求
为了让你更好的了解asyncio,我从最底层的socket开始写一个协程的get请求给你。
为了模拟延时很大的网站,我在本地开了一个延时服务器,这是服务器程序。
import tornado.ioloopimport tornado.webfrom tornado.gen import coroutine, sleepclass MainHandler(tornado.web.RequestHandler):
@coroutine
def get(self, waitTime=3):
yield sleep(int(waitTime))
self.write('you have waited for %ss' % waitTime)if __name__ == "__main__":
application = tornado.web.Application([
('/([0-9])', MainHandler),
], debug=True)
application.listen(5000)
try:
tornado.ioloop.IOLoop.current().start()
except:
tornado.ioloop.IOLoop.current().stop()
记得打开了这个服务器再运行下面的程序。
import socket, asyncio, timedata = 'GET /%s HTTP/1.1\r\n\r\n'loop = asyncio.get_event_loop()@asyncio.coroutinedef get(i):
future = asyncio.futures.Future(loop=loop)
s = socket.socket()
s.connect(('127.0.0.1', 5000))
s.sendall((data % i).encode('utf8'))
s.setblocking(False)
def callback(future):
future.set_result(s.recv(999).split(b'\r\n\r\n')[-1])
loop.add_reader(s.fileno(), callback, future)
r = yield from future
print('Return value: %s' % r)tasks = [get(3), get(3)]loop.run_until_complete(asyncio.wait(tasks))loop.close()
同步变为协程(线程池)
这里拿sleep模拟耗时的程序,原理就是开了5个新的线程处理耗时程序。
当然实际的`asyncio.sleep`只需要告诉消息循环一定时间后叫醒我就好了。
import asyncio, sleep, [email protected] sleep(i):
executor = concurrent.futures.ThreadPoolExecutor(5)
future = asyncio.futures.wrap_future(executor.submit(time.sleep, i), loop=loop)
yield from future
print('Slept for %s seconds' % i)tasks = [sleep(3), sleep(3)]loop.run_until_complete(asyncio.wait(tasks))loop.close()
⑵ python的socket的非阻塞实现
setblocking(0)之后就是非阻塞的。
select模块只是说能够同时处理多个socket,至于这些socket是阻塞还是非阻塞,都没有关系。当然从性能上考虑,现在的趋势是select+非阻塞。
⑶ python3套接字udp设置接受数据超时
Sometimes,you need to manipulate the default values of certain properties of a socket library, for example, the socket timeout.
设定并获取默认的套接字超时时间。
1.代码
1 import socket
2
3
4 def test_socket_timeout():
5 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
6 print("Default socket timeout: %s" % s.gettimeout())
7 # 获取套接字默认超时时间
8 s.settimeout(100)
9 # 设置超时时间
10 print("Current socket timeout: %s" % s.gettimeout())
11 # 读取修改后的套接字超时时间
12
13
14 if __name__ == '__main__':
15 test_socket_timeout()
2. AF_INET和SOCK_STREAM解释
1 # 地址簇
2 # socket.AF_INET IPv4(默认)
3 # socket.AF_INET6 IPv6
4 # socket.AF_UNIX 只能够用于单一的Unix系统进程间通信
5
6 # socket.SOCK_STREAM(数据流) 提供面向连接的稳定数据传输,即TCP/IP协议.多用于资料(如文件)传送。
3.gettimeout()和settimeout()解释
1 def gettimeout(self): # real signature unknown; restored from __doc__
2 """
3 gettimeout() -> timeout
4
5 Returns the timeout in seconds (float) associated with socket
6 operations. A timeout of None indicates that timeouts on socket
7 operations are disabled.
8 """
9 return timeout
10
11
12 def settimeout(self, timeout): # real signature unknown; restored from __doc__
13 """
14 settimeout(timeout)
15
16 Set a timeout on socket operations. 'timeout' can be a float,
17 giving in seconds, or None. Setting a timeout of None disables
18 the timeout feature and is equivalent to setblocking(1).
19 Setting a timeout of zero is the same as setblocking(0).
20 """
21 pass
22 # 设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。
23 # 一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )
4.运行结果
1 Default socket timeout: None
2 Current socket timeout: 100.0
⑷ python socketserver和socket的区别
区别:
1.首先介绍下socket
socket的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制,取后一种意思。通常也
称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。在Internet上的主机一 般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原 意那样,像一个多孔插座。一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就可以得到不同的服务
内部调用流程为:
启动服务端程序
执行 TCPServer.init方法,创建服务端Socket对象并绑定 IP 和 端口
执行 BaseServer.init方法,将自定义的继承自SocketServer.BaseRequestHandler 的类 - MyRequestHandle赋值给 self.RequestHandlerClass
执行 BaseServer.server_forever 方法,While 循环一直监听是否有客户端请求到达 ...
当客户端连接到达服务器
执行 ThreadingMixIn.process_request 方法,创建一个 “线程” 用来处理请求
执行 ThreadingMixIn.process_request_thread 方法
执行 BaseServer.finish_request 方法,执行 self.RequestHandlerClass() 即:执行 自定义 MyRequestHandler 的构造方法(自动调用基类BaseRequestHandler的构造方法,在该构造方法中又会调用 MyRequestHandler的handle方法)
ForkingTCPServer
ForkingTCPServer和ThreadingTCPServer的使用和执行流程基本一致,只不过在内部分别为请求者建立 “线程” 和 “进程”。
⑸ python select可以设置成非阻塞吗
setblocking(0)之后就是非阻塞的。 select模块只是说能够同时处理多个socket,至于这些socket是阻塞还是非阻塞,都没有关系。当然从性能上考虑,现在的趋势是select+非阻塞。
⑹ python socket 阻塞模式怎么确保数据recv
可以通过setsockopt,或者更简单的setblocking, settimeout设置。阻塞式的socket的recv服从这样的规则:
当缓冲区内有数据时,立即返回所有的数据;当缓冲区内无数据时,阻塞直到缓冲区中有数据。非阻塞式的socket的recv服从的规则则是:
当缓冲区内有数据时,立即返回所有的数据;当缓冲区内无数据时,产生EAGAIN的错误并返回(在Python中会抛出一个异常)。两种情况都不会返回空字符串,返回空数据的结果是对方关闭了连接之后才会出现的。
⑺ 小白求教,Python3实例类调用显示没定义
问下你的上述类代码与main代码都在同一个文件里吗,如果在不同文件,有没有import?
⑻ Python中socket里的.recv()函数问题
可以通过setsockopt,或者更简单的setblocking,
settimeout设置。阻塞式的socket的recv服从这样的规则:
当缓冲区内有数据时,立即返回所有的数据;当缓冲区内无数据时,阻塞直到缓冲区中有数据。非阻塞式的socket的recv服从的规则则是:
当缓冲区内有数据时,立即返回所有的数据;当缓冲区内无数据时,产生EAGAIN的错误并返回(在Python中会抛出一个异常)。两种情况都不会返回空字符串,返回空数据的结果是对方关闭了连接之后才会出现的。
⑼ python怎么做到socket的服务端和客户端可以同时发送数据
建两个socket不就可以了,模拟一个双向信道啊
⑽ python无法立即完成一个非阻止性套接字操作
你使用了非阻塞模式,而10035表示数据还没有返回给你。
你可以在接收前先select一下,如果有数据就接受,没有就跳过。
你可参考python官方文档
http://docs.python.org/howto/sockets.html
里面有Non-blocking Sockets的一章可以看看。
import select
...
while 1:
infds,outfds,errfds = select.select([s,],[],[],5)
if len(infds) >0:
....
else:
print "no data coming"