I have a piece of python code to practice python co-routines. As explained by A. Jesse Jiryu Davis.
- Firstly, I define a co-routine named 'get' to get the content of some URL.
- Then I define a Task class to iterate the co-routine to completion.
- Then I create two Tasks which open two different URL.
But I got the error message: KeyError: '368 (FD 368) is already registered' in the line selector.register(s.fileno(), EVENT_WRITE).
This error is caused by the same file descriptor is returned by the two calls to socket.socket(). Actually, this file descriptor 368 has been allocated in the previous call but still returned in the second call.
- Then I add an expression which modifies the outer variable.
This time the error message is just gone! If you want to run the code yourself, you can un-comment arr.append(self.init) in Task.step method to see the error-free output.
EDIT If I explicitly call python garbage collection, occasionally this error will be gone. But WHY occasionally?
After several days searching and reading python documents, I still have no idea why this is happening. I just missed some 'python Gotchas', do I?
I'm using python 3.6 to test. The code is as following and I have deleted all the irrelevant code to make the following code precise and relevant to the topic:
#! /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))
Task()->self.step->object->Task(). You built a circular reference and one of those objects has a reference to the generator, hence it will only be cleaned by the next garbage collector run. Which makes the bug appear non-deterministically.selector.register(s, EVENT_WRITE)should solve your problem. In that case,selectorholds the reference to the socket object.