新手 关于 Python 协程的一个问题

2020-12-07 11:27:01 +08:00
 IVeverKB

大概意思是这样的,协程里面有一个无线循环 A,主进程也就是下面写的 print main loop 是个无线循环 B 。有没有什么方法是 A 和 B 循环可以交替执行的?因为我现在发现只有让 A 一直跑或者 A 做完了才能做 B,但是因为它们俩都是死循环就没法实现同时交替执行。

或者是不是我要实现 A 和 B 交替循环只有把 A 写成多线程才行?前提是我不想把 B 写进另一个协程或线程里,我只想让它保留在主进程这个地方。

多谢

import asyncio
import time

async def my_async():
	while True:
    		await asyncio.sleep(0.5)
        	print("in my_async loop")


loop = asyncio.get_event_loop()
loop.crate_task(my_async())

loop.run_forever()
#loop.run_until_complete(my_async())

while True:
	time.sleep(0.5)
	print("in the main loop")
2343 次点击
所在节点    Python
14 条回复
111111111111
2020-12-07 11:36:08 +08:00
因为实际上是单线程的,下面的 while 要等上面的代码执行完了才有机会执行。
或许可以试试 yield 关键字?

你说的“不想把 B 写进另一个协程或线程里,我只想让它保留在主进程这个地方” 你可以说说具体的原因,这里只有一个进程一个线程,没什么主不主的
sujin190
2020-12-07 11:39:50 +08:00
把 B 也用协程就是了啊,协程调度本来就独占整个线程的
Vegetable
2020-12-07 11:41:14 +08:00
按你说的没办法,你这个设计有问题,要不就 A 进独立线程,要不就 ab 都在一个 event loop 里
fasionchan
2020-12-07 11:45:35 +08:00
协程需要由事件循环驱动,如果主线程执行了事件循环,就没有机会执行外部的 while True,如果执行了 while True,就无法执行事件循环。
IVeverKB
2020-12-07 11:54:02 +08:00
@111111111111 #1
@sujin190 #2
我是把业务需求简化了才这么说的。现在是想要在 django 启动的时候开一个协程或线程同时处理一下其他事情,django 本身对我来说就相当于 B 循环,所以我就这么问了。实际上是我不会把 B,也就是 django 本身业务写成一个协程。不知道这么说容易理解不。
IVeverKB
2020-12-07 11:58:17 +08:00
@Vegetable #3
@fasionchan #4
感谢,明白了协程肯定做不了这个了
NeilWang
2020-12-07 12:27:29 +08:00
真的想使用协程运行任务 A 的话,其实可以在 django 启动前启动一个子线程,在子线程中创建一个新的事件循环,然后把协程 A 在这个事件循环里运行。

```
import asyncio
import time
import threading


async def my_async():
while True:
await asyncio.sleep(0.5)
print("in my_async loop")


_event_loop = asyncio.new_event_loop()


def run_event_loop():
_event_loop.run_forever()


threading.Thread(target=_event_loop.run_forever, daemon=True).start()

# 将新的事件循环设置为当前线程的事件循环,这样后续在当前线程中可以使用 asyncio.get_event_loop() 得到 _event_loop
asyncio.set_event_loop(_event_loop)

# 事件循环不在当前线程,需要使用 run_coroutine_threadsafe 提交协程任务
asyncio.run_coroutine_threadsafe(my_async(), asyncio.get_event_loop())

while True:
time.sleep(0.5)
print("in the main loop")
```

这样的话,在 django 中处理视图逻辑时也是可以提交协程任务到事件循环的
IVeverKB
2020-12-07 14:51:58 +08:00
@NeilWang #7
非常感谢!提供了新思路,这个想法也太妙了哈哈
keepeye
2020-12-07 15:00:27 +08:00
A 也转换成异步的 然后 asyncio.gather(A(), B())

A 里面 sleep 换成 asyncio.sleep
no1xsyzy
2020-12-07 15:08:17 +08:00
我怀疑你应当寻求 Celery 一类任务框架

顺便一提,loop.run_forever() 本身就可以理解为一个 while 循环。
sujin190
2020-12-07 15:33:21 +08:00
@IVeverKB #5 你需要的是 celery
IVeverKB
2020-12-07 16:51:22 +08:00
@NeilWang #7
能否再追问一下,这个子线程写在 django 项目的哪个文件中比较好呢,就是能做到不影响 django 本项目的运行但同时还能被顺利执行到(且只会被执行一次,因为之前有看到写在 urls.pyviews.py 中会因为部署在 uwsgi+nginx 而产生多进程执行多次)?目前两个备选,一个是 project 目录下的__init__.py 文件,或者是 wsgi.py 文件,不知道是否满足我的需求?
ClericPy
2020-12-07 22:04:54 +08:00
交替执行... 两个任务的话, 办法挺多的, yield 生成器 / 或者互相 yield / asyncio.Event, 但是具体哪个能用还是看具体场景, 你这例子都没举全, 也没法给你调试个代码出来
SaltCat
2020-12-07 22:54:12 +08:00
用 asyncio.Lock,还有就直接 asyncio 技术栈一把嗦吧,asyncio 的侵入性感觉还是太强了。。。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/732859

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX