在Windows下使用asyncio模块(事件循环用的是默认的_WindowsSelectorEventLoop
)
探究其实现,发现其中调用了connect后,还调用了select。
问题是,本来在程序等待三次握手的时候,Python可以把执行别的协程的,但是这样同步的调用,使得Python只能等待三次握手了。
这样会造成性能问题吗?
如果在异步上下文中调用了阻塞函数,确实会造成性能问题。不过呢,这里的 socket.connect
是非阻塞的,所以不会有性能问题。见 asyncio/base_events.py
393 行:
sock = socket.socket(family=family, type=type, proto=proto)
sock.setblocking(False)
这里设置了 socket 对象为非阻塞的,所以你看到的下面这些代码都会立即返回:
res = connect(s->sock_fd, addr, addrlen); // <<<<<<<< 1
#ifdef MS_WINDOWS
if (s->sock_timeout > 0.0) {
if (res < 0 && WSAGetLastError() == WSAEWOULDBLOCK &&
IS_SELECTABLE(s)) {
/* This is a mess. Best solution: trust select */
fd_set fds;
fd_set fds_exc;
struct timeval tv;
tv.tv_sec = (int)s->sock_timeout;
tv.tv_usec = (int)((s->sock_timeout - tv.tv_sec) * 1e6);
FD_ZERO(&fds);
FD_SET(s->sock_fd, &fds);
FD_ZERO(&fds_exc);
FD_SET(s->sock_fd, &fds_exc);
res = select(Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int),
NULL, &fds, &fds_exc, &tv); // <<<<<<<< 2
if (res == 0) {
res = WSAEWOULDBLOCK; // <<<<<<<<<< 3
timeout = 1;
} else if (res > 0) {
——即使三次握手还没有完成。异步等待握手完成的代码见 asyncio/selector_events.py
的 308 行:
except (BlockingIOError, InterruptedError):
self.add_writer(fd, self._sock_connect, fut, True, sock, address)