1. maueki

    No comment

    maueki
Changes in body
Source | HTML | Preview
@@ -1,121 +1,123 @@
+## ここは書きかけ
+
C#においては`async`は内部に`await`を書けるという以外何もしないが(要出典)、pythonの`async def`は`def`とは機能が異なっている。
```py:hello.py
import asyncio
async def hello_world():
print("Hello World!")
hello_world()
```
```bash
$ python3 hello.py
hello.py:6: RuntimeWarning: coroutine 'hello_world' was never awaited
hello_world()
```
pythonにおいて`async def`は[コルーチン関数を定義するための構文](https://docs.python.org/ja/3/reference/compound_stmts.html#coroutine-function-definition)であり 、[コルーチンはイベントループ内でしか実行されない](https://docs.python.org/ja/3/library/asyncio-task.html#coroutines)。
## `await future`と`await coroutine`
[18.5.3.1. コルーチン](https://docs.python.org/ja/3/library/asyncio-task.html#coroutines)を読んでいると気になる記述に出くわす。
> コルーチンができること:
>
> * `result = await future` or `result = yield from future` -- suspends the coroutine until the future is done, then returns the future's result, or raises an exception, which will be propagated. (If the future is cancelled, it will raise a CancelledError exception.) Note that tasks are futures, and everything said about futures also applies to tasks.
> * `result = await coroutine` or `result = yield from coroutine` -- wait for another coroutine to produce a result (or raise an exception, which will be propagated). The coroutine expression must be a call to another coroutine.
どうやらpythonの`await`は`future`と`coroutine`の2つが取れるらしいが、挙動が微妙に違う。
`await future`の方はsuspends、つまり制御を手放すのに対して`await coroutine`の方は単にcoroutineの終了を待つだけに見える。
ためしに以下のようなコードを書いてみる。
```python:await_sample.py
import asyncio
import time
async def task_one():
print("task_one: before sleep")
await asyncio.sleep(0.1)
print("task_one: after sleep")
return 1
async def time_sleep():
time.sleep(5)
print("time_sleep")
async def task_two():
print("task_two: before task_one")
await time_sleep()
print("task_two: after task_one")
return 2
async def test(loop):
t1 = loop.create_task(task_one())
t2 = loop.create_task(task_two())
print(repr(await t1))
print(repr(await t2))
def main():
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(test(loop))
finally:
loop.close()
main()
```
```bash
$ python3 await_sample.py
task_one: before sleep
task_two: before task_one
# ここで5秒待つ
time_sleep
task_two: after task_one
task_one: after sleep
1
2
```
挙動としては、一見以下のように見える
1. `task_one()`が実行され、`await asyncio.sleep(0.1)`で制御を手放す
2. `task_two()`が開始されるが`await time_sleep()`では制御を手放さずそのまま寝て返ってくる
`asyncio.sleep()`は[コルーチンである](https://docs.python.org/ja/3/library/asyncio-task.html#asyncio.sleep)。また、`time_sleep()`もコルーチンのはずである。片方は制御を手放し、片方は制御を手放さない。これはどういうことだろうか?
ポイントは`asyncio.sleep()`の実装にある。
```python:cpython/Lib/asyncio/tasks.py
async def sleep(delay, result=None, *, loop=None):
"""Coroutine that completes after a given time (in seconds)."""
if delay <= 0:
await __sleep0()
return result
if loop is None:
loop = events.get_event_loop()
future = loop.create_future()
h = loop.call_later(delay,
futures._set_result_unless_cancelled,
future, result)
try:
return await future
finally:
h.cancel()
```
`return await future`があるのが見て取れる。
冒頭の記述を思い出して解釈するとサンプルコードの挙動は以下のようになる。
1. `task_one()`が実行され、`await asyncio.sleep(0.1)`によりコルーチンである`asyncio.sleep()`内部が実行される
2. `asyncio.sleep()`内部の`await future`で制御を手放す
3. task_two()`が開始され`await time_sleep()`により、コルーチンである`time_sleep()`内部が実行される
4. `time_sleep()`内には制御を手放す構文はないため`time.sleep(5)`ごとそのまま実行され呼び出し元に返る
5.