简体   繁体   English

在主线程运行的asyncio事件循环中运行无限循环

[英]run infinite loop in asyncio event loop running off the main thread

I wrote code that seems to do what I want, but I'm not sure if it's a good idea since it mixes threads and event loops to run an infinite loop off the main thread. 我编写的代码似乎做了我想要的,但我不确定它是不是一个好主意,因为它混合线程和事件循环来运行主线程的无限循环。 This is a minimal code snippet that captures the idea of what I'm doing: 这是一个最小的代码片段,它捕获了我正在做的事情的想法:

import asyncio
import threading

msg = ""

async def infinite_loop():
    global msg
    while True:
        msg += "x"
        await asyncio.sleep(0.3)

def worker():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    asyncio.get_event_loop().run_until_complete(infinite_loop())

t = threading.Thread(target=worker, daemon=True)
t.start()

The main idea is that I have an infinite loop manipulating a global variable each 0.3 s. 主要思想是我有一个无限循环,每0.3秒操纵一个全局变量。 I want this infinite loop to run off the main thread so I can still access the shared variable in the main thread. 我希望这个无限循环运行主线程,所以我仍然可以访问主线程中的共享变量。 This is especially useful in jupyter, because if I call run_until_complete in the main thread I can't interact with jupyter anymore. 这在jupyter中特别有用,因为如果我在主线程中调用run_until_complete ,我就不能再与jupyter交互了。 I want the main thread available to interactively access and modify msg . 我希望主线程可以交互式访问和修改msg Using async might seem unnecessary in my example, but I'm using a library that has async code to run a server, so it's necessary. 在我的示例中使用async似乎是不必要的,但是我使用的是具有异步代码的库来运行服务器,所以这是必要的。 I'm new to async and threading in python, but I remember reading / hearing somewhere that using threading with asyncio is asking for trouble... is this a bad idea? 我是python中异步和线程的新手,但我记得在某处读取/听到使用asyncio进行线程处理时遇到麻烦......这是个坏主意吗? Are there any potential concurrency issues with my approach? 我的方法是否存在任何潜在的并发问题?

I'm new to async and threading in python, but I remember reading / hearing somewhere that using threading with asyncio is asking for trouble... 我是python中异步和线程的新手,但我记得在某处读取/听到使用asyncio进行线程处理时遇到麻烦...

Mixing asyncio and threading is discouraged for beginners because it leads to unnecessary complications and often stems from a lack of understanding of how to use asyncio correctly. 初学者不鼓励混合使用asyncio和线程,因为它会导致不必要的并发症,并且通常源于对如何正确使用asyncio缺乏了解。 Programmers new to asyncio often reach for threads by habit, using them for tasks for which coroutines would be more suitable. 熟悉asyncio的程序员经常习惯于线程,将它们用于协同程序更适合的任务。

But if you have a good reason to spawn a thread that runs the asyncio event loop, by all means do so - there is nothing that requires the asyncio event loop to be run in the main thread. 但是如果你有充分的理由产生运行asyncio事件循环的线程,那么一定要这样做 - 没有什么需要在主线程中运行asyncio事件循环。 Just be careful to interact with the event loop itself (call methods such as call_soon , create_task , stop , etc.) only from the thread that runs the event loop, ie from asyncio coroutines and callbacks. 注意只能从运行事件循环的线程(即asyncio协程和回调)中与事件循环本身(调用call_sooncreate_taskstop等等)进行交互。 To safely interact with the event loop from the other threads, such as in your case the main thread, use loop.call_soon_threadsafe() or asyncio.run_coroutine_threadsafe() . 要安全地与来自其他线程的事件循环交互,例如在主线程中,请使用loop.call_soon_threadsafe()asyncio.run_coroutine_threadsafe()

Note that setting global variables and such doesn't count as "interacting" because asyncio doesn't observe those. 请注意,设置全局变量等不会计为“交互”,因为asyncio不会观察到这些。 Of course, it is up to you to take care of inter-thread synchronization issues, such as protecting access to complex mutable structures with locks. 当然,由您来处理线程间同步问题,例如使用锁保护对复杂可变结构的访问。

is this a bad idea? 这是一个坏主意吗?

If unsure whether to mix threads and asyncio, you can ask yourself two questions: 如果不确定是否混合线程和asyncio,你可以问自己两个问题:

  • Do I even need threads, given that asyncio provides coroutines that run in parallel and run_in_executor to await blocking code? 我是否甚至需要线程,因为asyncio提供并行运行的协程和run_in_executor来等待阻塞代码?
  • If I have threads providing parallelism, do I actually need asyncio? 如果我有提供并行性的线程,我真的需要asyncio吗?

Your question provides good answers to both - you need threads so that the main thread can interact with jupyter, and you need asyncio because you depend on a library that uses it. 你的问题提供了很好的答案 - 你需要线程,以便主线程可以与jupyter交互,你需要asyncio,因为你依赖于使用它的库。

Are there any potential concurrency issues with my approach? 我的方法是否存在任何潜在的并发问题?

The GIL ensures that setting a global variable in one thread and reading it in another is free of data races, so what you've shown should be fine. GIL确保在一个线程中设置一个全局变量并在另一个线程中读取它没有数据竞争,所以你所展示的应该没问题。

If you add explicit synchronization, such as a multi-threaded queue or condition variable, you should keep in mind that the synchronization code must not block the event loop. 如果添加显式同步(例如多线程队列或条件变量),则应记住同步代码不得阻止事件循环。 In other words, you cannot just wait on, say, a threading.Event in an asyncio coroutine because that would block all coroutines. 换句话说,你不能只等待,例如,asyncio协程中的threading.Event ,因为这会阻止所有协同程序。 Instead, you can await an asyncio.Event , and set it using something like loop.call_soon_threadsafe(event.set) from the other thread. 相反,您可以等待asyncio.Event ,并使用来自其他线程的loop.call_soon_threadsafe(event.set)类的东西进行设置。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM