gevent 模块生成的 greenlet 对象,要如何启动?

以前以为:gevent 模块生成的 greenlet 对象,需要用 join 或 joinall 来启动。例如:

# /usr/bin/env python3
# -*- encoding:utf-8 -*-

from gevent import monkey; monkey.patch_all()
import gevent,urllib.request

def f(url):
    print('GET: %s' % url)
    resp = urllib.request.urlopen(url)
    data = resp.read()
    print('%d bytes received from %s.' % (len(data), url))

gevent.joinall([
        gevent.spawn(f, 'https://www.python.org/'),
        gevent.spawn(f, 'https://www.yahoo.com/'),
        gevent.spawn(f, 'https://github.com/'),
])

但是有一次看到一个 “通过单线程实现多socket并发” 的例子:

服务端:

# /usr/bin/env python3
# -*- encoding:utf-8 -*-

import socket, gevent
from gevent import socket, monkey;monkey.patch_all()


def server(port):
    s = socket.socket()
    s.bind(('0.0.0.0', port))
    s.listen(500)
    while True:
        cli, addr = s.accept()
        gevent.spawn(handle_request, cli)


def handle_request(conn):
    try:
        while True:
            data = conn.recv(1024)
            print("recv:", data)
            conn.send(data)
            if not data:
                conn.shutdown(socket.SHUT_WR)

    except Exception as  ex:
        print(ex)
    finally:
        conn.close()


if __name__ == '__main__':
    server(8001)

客户端

# /usr/bin/env python3
# -*- encoding:utf-8 -*-

import socket

HOST = 'localhost'
PORT = 8001
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while True:
    msg = bytes(input(">>:"), encoding="utf8")
    s.sendall(msg)
    data = s.recv(1024)
    print('Received:',data.decode('utf-8'))

试着启动了一个服务端,多个客户端,与多个客户端的交互确实可以并行。但是服务端那里,只是生成了 greenlet 对象(gevent.spawn(handle_request, cli)),并没有 join 或 joinall,为什么也可以启动 spawn 生成的协程?

我又试着把第一个爬取网页的例子,去掉了 joinall ,像这样:

# /usr/bin/env python3
# -*- encoding:utf-8 -*-

# 第一个例子去掉 joinall ,结果并不执行

from gevent import monkey; monkey.patch_all()
import gevent,urllib.request

def f(url):
    print('GET: %s' % url)
    resp = urllib.request.urlopen(url)
    data = resp.read()
    print('%d bytes received from %s.' % (len(data), url))


gevent.spawn(f, 'https://www.python.org/')
gevent.spawn(f, 'https://www.yahoo.com/')
gevent.spawn(f, 'https://github.com/')

仅仅是 spawn 了3个协程对象,果然就不执行了,不解啊,为什么第一个爬取网页的例子没有 joinall 就不行,而第二个socket服务器的例子就不需要 joinall 啊

阅读 4.3k
2 个回答

因为它在一个 while True 里,永远也不会退出(就算是想 join 都没机会)。

顾名思义,spawn 就是启动了。join 只是等待它们结束而已。你不想等也是可以的,但是你直接退出的话它们也就都死掉了。

新手上路,请多包涵

我猜是因为accept()执行的时候让出了CPU吧。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题