我有一段python代码来练习python协同例程。 如A. Jesse Jiryu Davis所解释。在
但我收到了错误信息: KeyError:“368(FD 368)已在行中注册”选择器寄存器(s.fileno(),EVENT_WRITE)。在
此错误是由以下两个调用返回的相同文件描述符引起的插座.插座(). 实际上,这个文件描述符368已经在前一次调用中被分配,但在第二次调用中仍然返回。在
这一次错误信息消失了! 如果你想自己运行代码,你可以取消注释附件(self.init)在任务.步骤方法查看无错误输出。在
EDIT如果我显式调用python垃圾回收,偶尔这个错误就会消失。为什么偶尔呢?
在搜索和阅读python文档几天后,我仍然不知道为什么会发生这种情况。我错过了一些“Python陷阱”,是吗?在
我使用python3.6进行测试。代码如下,我删除了所有不相关的代码,以使下面的代码更加精确并与主题相关:
#! /usr/bin/python
from selectors import DefaultSelector, EVENT_WRITE
import socket
import gc
selector = DefaultSelector()
arr = [1, 2, 3]
class Task:
def __init__(self, gen):
self.gen = gen
self.step()
def step(self):
next(self.gen)
# arr.append(self.__init__)
def get(path, count = 0):
s = socket.socket()
print(count, 'fileno:', s.fileno())
s.connect(('www.baidu.com', 80))
selector.register(s.fileno(), EVENT_WRITE)
yield
Task(get('/foo',1))
gc.collect()
Task(get('/bar',2))
@何艾伦,非常感谢你的回复。在
来自@Daniel Roseman的配额,请参见:
how to prevent Python garbage collection for anonymous objects?
请参见:Why python doesn't have Garbage Collector thread?
否,插座.插座()不会返回已占用的文件描述符。如果一个fd已经被释放,这意味着一个fd已经被释放了。在
注:
get('/foo', 1)
是匿名生成器对象,Task(get('/foo', 1))
是匿名Task
对象。在原始代码的引用链是:
因此,匿名
Task(get('/foo', 1))
对象一完成就被GC收集。这是因为:那么匿名的}。在这里,
get('/foo', 1)
将被收集,然后是{s
被收集、关闭,其对应的套接字fd编号(在您的示例中是#368)已被释放。在但是套接字fd号(#368)已注册到
selector
。在然后运行
Task(get('/bar',2))
,一个新的socket s
试图申请一个“新”fd,因为#368是可用的(只要系统中的其他进程没有声明它),您将得到#368作为套接字fd。在在中取消注释
arr.append(self.__init__)
任务.步骤()在中取消注释
^{pr2}$arr.append(self.__init__)
后任务.步骤()方法,全局arr
包含对Task(get('/foo', 1))
的引用。那么Task(get('/foo', 1))
引用了get('/foo', 1)
。然后get('/foo', 1)
引用了本地套接字s
。该参考链如下:arr
通过您的程序是有效的,因此s
将不会被GC收集。后面的s = socket.socket()
将不会得到相同的fd
,因为它仍然由{使用
s
代替s.fileno()
如果使用
selector.register(s ..)
而不是selector.register(s.fileno()..)
,全局selector
将保存对本地s
的引用,引用链是:虽然这两个匿名对象已经不存在,但是您的}仍然由全局
get('/foo', 1))::s
和{selector
持有。所以不要担心这两个fd
不会碰撞。在循环参考?在
答案是否定的。你的情况与循环引用无关。在
在收集gc?在
好吧,用
time.sleep(0.02)
代替它,你会观察到同样的现象。这可能是由于:s
,或者线程正在收集。在相关问题 更多 >
编程相关推荐