【问题标题】:Python calling coroutine from normal functionPython从普通函数调用协程
【发布时间】:2018-02-26 16:59:29
【问题描述】:

所以我有一个倒计时脚本,看起来像这样:

import time, threading, asyncio
def countdown(n, m):
    print("timer start")
    time.sleep(n)
    print("timer stop")
    yield coro1

async def coro1():
    print("coroutine called")

async def coromain():
    print("first")
    t1 = threading.Thread(target=countdown, args=(5, 0))
    t1.start()
    print("second")

loop = asyncio.get_event_loop()
loop.run_until_complete(coromain())
loop.stop()

我想要它做的很简单:

Run coromain
Print "first"
Start thread t1, print "timer start" and have it wait for 5 seconds
In the mean time, print "second"
after 5 seconds, print "timer stop"
exit

但是,当我运行此代码时,它会输出:

Run coromain
Print "first"
Print "second"
exit

我很困惑为什么会这样。谁能解释我在这里做错了什么?

【问题讨论】:

  • 你能解释一下在不同线程中运行倒计时的选择吗?
  • 我正在制作一个不和谐的机器人。我需要它倒计时 n 秒然后通知用户,同时仍然能够在主线程中接受命令

标签: python multithreading python-3.6 python-asyncio


【解决方案1】:

这取决于您的问题是否属于施加额外限制的更大问题的一部分,但我认为没有理由使用threading。相反,您可以使用两个独立的Tasks 在同一个事件循环中运行,这是异步编程的要点之一:

import asyncio

async def countdown(n, m):  # <- coroutine function
    print("timer start")
    await asyncio.sleep(n)
    print("timer stop")
    await coro1()

async def coro1():
    print("coroutine called")

async def coromain():
    print("first")
    asyncio.ensure_future(countdown(5, 0))  # create a new Task
    print("second")

loop = asyncio.get_event_loop()
loop.run_until_complete(coromain())  # run coromain() from sync code
pending = asyncio.Task.all_tasks()  # get all pending tasks
loop.run_until_complete(asyncio.gather(*pending))  # wait for tasks to finish normally

输出:

first
second
timer start
(5 second wait)
timer stop
coroutine called

当使用ensure_future 时,您有效地在单个操作系统的线程中创建了一个新的“执行线程”(请参阅​​纤程)。

【讨论】:

  • 太棒了,我对 asyncio 不是很熟悉,但是因为我使用的库只能与所述库一起使用,所以我不得不这样做。我认为必须有这样的方法,但无法从文档中找出来
  • 当你传递一个实际的协程时,你可能想要使用create_task 而不是ensure_futureAccording to Guido, ensure_future 仅在您转换可能是也可能不是未来的东西并且需要实际的Future 结果时才需要,例如拨打canceladd_done_callback就可以了。
  • @user4815162342 这很有趣,谢谢。我通常使用ensure_future,因为我很少有明确的循环。
  • 这是有道理的,但是ensure_future 有任何create_task 没有的开销吗? Aka,当您知道您正在处理 Future 时,使用 create_task 会更好吗?
  • @DavinMiler 在这种情况下,它不是关于开销,而是关于代码的清晰度。 ensure_future 表示代码正在尝试将某个对象转换为未来。 create_task 总是接受一个协程并返回一个任务,它是Future 的一个子类,它在事件循环中“驱动”一个协程。现在,ensure_future 足够聪明,可以在给定协程时返回任务,但使用 create_task 更准确地表明了代码的意图。
【解决方案2】:

经过一番挖掘制作了这个解决方法。它可能不漂亮,但它有效:

import time, threading, asyncio
def countdown(n, m):
    print("timer start")
    time.sleep(n)
    print("timer stop")
    looptemp = asyncio.new_event_loop()
    asyncio.set_event_loop(looptemp)
    loop2 = asyncio.get_event_loop()
    loop2.run_until_complete(coro1())
    loop2.close()

async def coro1():
    print("coroutine called")

async def coromain():
    print("first")
    t1 = threading.Thread(target=countdown, args=(5, 0))
    t1.start()
    print("second")

loop = asyncio.get_event_loop()
loop.run_until_complete(coromain())
loop.stop()

不幸的是,它不适用于我的特定用例,但我认为它可能有用。

【讨论】:

    猜你喜欢
    • 2019-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-15
    • 2017-09-19
    • 2017-05-06
    • 1970-01-01
    相关资源
    最近更新 更多