python 3.4 新加入的asyncio是咋通過yield from實現非同步的?

import asyncio

@asyncio.coroutine
def hello():
print("Hello world!")
# 非同步調用asyncio.sleep(1):
r = yield from asyncio.sleep(1) # 這裡為啥線程不等待asyncio.sleep(1)執行完
print("Hello again!")

# 獲取EventLoop:
loop = asyncio.get_event_loop()
# 執行coroutine
loop.run_until_complete(hello())
loop.close()

"""hello()會首先列印出Hello world!,然後,yield from語法可以讓我們"""
"""方便地調用另一個generator。由於asyncio.sleep()也是一個coroutine,"""
"""所以線程不會等待asyncio.sleep(),而是直接中斷並執行下一個消息循環。"""
"""當asyncio.sleep()返回時,線程就可以從yield from拿到返回值(此處是None),"""
"""然後接著執行下一行語句。"""

"""不理解由於asyncio.sleep()也是一個coroutine,所以線程不會等待"""
"""asyncio.sleep() 這句話。。。求解釋"""


先忽略 python 3.4 這個具體的版本, 在 python 裡面, generator 與 coroutine 的關係,在下面這個頁面里給了具體的解釋(通過 yield、send 來實現)。

A Curious Course on Coroutines and Concurrency

你把作者提供的 pdf 看完,尤其是 77 頁 到 84 頁, 他用很少的篇幅,自己實現了一個 coroutine。


看一下源代碼里asyncio.sleep是怎麼實現的你會大吃一驚…


我這幾天也在看 asyncio 下面是我理解 :

asyncio.sleep(1) 沒有等待的原因,應該是 sleep 並沒有真的在「占著床位(線程)在睡覺」,而是 sleep 在運行的一開始就立馬 yield 了一個(大概)是 future 一樣的東西,告訴調度器:1 秒以後再試著來找我(call_at()?),然後就讓出線程了,調度器接著干他自己的事兒。1 秒後如果線程不忙的話用 send(x) 來喚起 coro hello 。

假如 asyncio.sleep 的實現方式,是傳統的「計時器」的方式,那麼 sleep 依舊會佔著線程,調度器也必須一直等著它「睡完」。

不十分確定,有問題歡迎指出。


因為asyncio.sleep(1)是協程,協程在線程下可以切換執行,不會等待


asyncio實現的方式可能會讓你哭笑不得,並沒有什麼真的線程,就是不斷重複執行tasks.py文件里的_step函數,asyncio.sleep就是重複執行直到手動計算超時,然後結束。所以你要是用time.sleep不僅沒法非同步,還會報錯。

附上_step的代碼,關鍵是要看懂send函數

def _step(self, exc=None):
assert not self.done(),
"_step(): already done: {!r}, {!r}".format(self, exc)
if self._must_cancel:
if not isinstance(exc, futures.CancelledError):
exc = futures.CancelledError()
self._must_cancel = False
coro = self._coro
self._fut_waiter = None

self.__class__._current_tasks[self._loop] = self
# Call either coro.throw(exc) or coro.send(None).
try:
if exc is None:
# We use the `send` method directly, because coroutines
# don"t have `__iter__` and `__next__` methods.
result = coro.send(None)
else:
result = coro.throw(exc)
except StopIteration as exc:
self.set_result(exc.value)
except futures.CancelledError as exc:
super().cancel() # I.e., Future.cancel(self).
except Exception as exc:
self.set_exception(exc)
except BaseException as exc:
self.set_exception(exc)
raise
else:
if isinstance(result, futures.Future):
# Yielded Future must come from Future.__iter__().
if result._loop is not self._loop:
self._loop.call_soon(
self._step,
RuntimeError(
"Task {!r} got Future {!r} attached to a "
"different loop".format(self, result)))
elif result._blocking:
result._blocking = False
result.add_done_callback(self._wakeup)
self._fut_waiter = result
if self._must_cancel:
if self._fut_waiter.cancel():
self._must_cancel = False
else:
self._loop.call_soon(
self._step,
RuntimeError(
"yield was used instead of yield from "
"in task {!r} with {!r}".format(self, result)))
elif result is None:
# Bare yield relinquishes control for one event loop iteration.
self._loop.call_soon(self._step)
elif inspect.isgenerator(result):
# Yielding a generator is just wrong.
self._loop.call_soon(
self._step,
RuntimeError(
"yield was used instead of yield from for "
"generator in task {!r} with {}".format(
self, result)))
else:
# Yielding something else is an error.
self._loop.call_soon(
self._step,
RuntimeError(
"Task got bad yield: {!r}".format(result)))
finally:
self.__class__._current_tasks.pop(self._loop)
self = None # Needed to break cycles when an exception occurs.


一個非同步web爬蟲的實現 A Web Crawler With asyncio Coroutines

asyncio中的協程實現基於生成器,Future類和"yield from"


coroutine asyncio.sleep(delay, result=None, *,
loop=None)

Create a coroutine that completes after a given time
(in seconds). If result is provided, it is produced to the caller when
the coroutine completes.

The resolution of the sleep depends on the granularity of the
event loop.(

Delayed calls)

This function is a coroutine.


推薦閱讀:

如何看待 Instagram 將所有 Web 後端遷移到 Python 3.6 ?
Python混合類型計算中的一點疑惑?
Python 3.6 有什麼新特性?
Python2轉Python3有沒有必要重新學習?
VPS 如何搭建 Python 環境?

TAG:Python | Python3x | Python庫 |