什么时候应该在常规线程上使用异步,为什么?它能提高性能吗?

2024-05-16 05:17:25 发布

您现在位置:Python中文网/ 问答频道 /正文

我对Python中的多线程有相当基本的理解,对asyncio也有更基本的er理解。

我目前正在编写一个基于诅咒的小程序(最终将使用完整的GUI,但这是另一个故事),该程序处理主线程中的UI和用户IO,然后有两个其他守护进程线程(每个线程都有自己的队列/工作方法-从队列中获取信息):

  • 一个watcher线程,它监视将要发生的基于时间和条件的事件(如发送到消息板、接收到的消息等),然后将所需的任务放入。。。
  • 另一个(worker)守护进程线程的队列,然后完成它们。

这三个线程都在同时运行,这就引出了一些问题:

  • worker线程的队列(或者更一般地说,任何线程的队列)为空时,应该停止它,直到它有事情要做,还是可以继续运行?当并发线程除了监视其队列之外不做任何事情时,它们是否会占用大量的处理能力?
  • 两个线程的队列应该合并吗?由于watcher线程持续运行一个方法,我猜worker线程将能够从watcher线程放入的单个队列中提取任务。
  • 我认为这无关紧要,因为我不是多处理,但这个设置是否受到Python的GIL(我相信它仍然存在于3.4中)的任何影响?
  • 这个watcher线程应该像那样连续运行吗?根据我的理解,如果我错了,请纠正我,asyncio应该用于基于事件的多线程处理,这似乎与我要做的事情有关。
  • 主线程基本上总是在等待用户按键访问菜单的不同部分。这似乎是一种完美的情况,但我不确定。

谢谢!


Tags: 方法用户程序asyncio消息队列进程事件
1条回答
网友
1楼 · 发布于 2024-05-16 05:17:25

When the worker thread's queue (or, more generally, any thread's queue) is empty, should it be stopped until is has something to do again, or is it okay to leave continuously running? Do concurrent threads take up a lot of processing power when they aren't doing anything other than watching its queue?

您应该只使用阻塞调用queue.get()。这将使线程在I/O上阻塞,这意味着GIL将被释放,并且不会使用任何处理能力(或者至少是非常少量的处理能力)。不要在while循环中使用非阻塞gets,因为这需要更多的CPU唤醒。

Should the two threads' queues be combined? Since the watcher thread is continuously running a single method, I guess the worker thread would be able to just pull tasks from the single queue that the watcher thread puts in.

如果观察者所做的一切只是从队列中取出一些东西,然后立即将其放入另一个队列中,在该队列中它会被一个工作线程消耗掉,那么听起来这是不必要的开销—您也可以直接在工作线程中消耗它。不过,我不太清楚情况是否如此,观察者是在排队消费,还是只是把物品放进一个队列?如果它是从队列中消费的,谁会把东西放进去?

I don't think it'll matter since I'm not multiprocessing, but is this setup affected by Python's GIL (which I believe still exists in 3.4) in any way?

是的,这是受吉尔的影响。一次只能有一个线程运行Python字节码,因此无法获得真正的并行性,除非线程运行I/O(这会释放GIL)。如果您的工作线程正在执行CPU绑定的活动,您应该认真考虑通过multiprocessing在单独的进程中运行它,如果可能的话。

Should the watcher thread be running continuously like that? From what I understand, and please correct me if I'm wrong, asyncio is supposed to be used for event-based multithreading, which seems relevant to what I'm trying to do.

很难说,因为我不知道“连续跑步”到底是什么意思。它一直在做什么?如果它把大部分时间都花在睡眠或阻塞上,那就没问题了——这两样东西都会释放GIL。如果它一直在做实际的工作,那就需要GIL,从而降低应用程序中其他线程的性能(假设它们试图同时做工作)。asyncio是为I/O绑定的程序设计的,因此可以在单个线程中运行,使用异步I/O。根据您的worker所做的操作,您的程序可能非常适合这种情况。

The main thread is basically always just waiting for the user to press a key to access a different part of the menu. This seems like a situation asyncio would be perfect for, but, again, I'm not sure.

任何一个主要等待I/O的程序都有可能适合于asyncio-但前提是你能找到一个能让诅咒(或者你最终选择的任何其他GUI库)很好地使用它的库。大多数GUI框架都有自己的事件循环,这将与asyncio的事件循环冲突。您需要使用一个库,该库可以使GUI的事件循环与asyncio的事件循环很好地配合。您还需要确保可以找到应用程序使用的任何其他基于同步I/O的库的asyncio兼容版本(例如数据库驱动程序)。

也就是说,从基于线程的程序切换到基于asyncio的程序,您不太可能看到任何性能改进。它可能也会有同样的表现。因为您只处理3个线程,所以在它们之间切换上下文的开销不是很大,所以从这一点切换到单线程异步I/O方法不会有很大的区别。asyncio将有助于避免线程同步的复杂性(如果这是你的应用程序的一个问题-目前还不清楚),并且至少在理论上,如果你的应用程序可能需要大量的线程,那么它的伸缩性会更好,但事实似乎并非如此。我认为对于您来说,基本上取决于您喜欢使用哪种样式(假设您可以找到所需的所有asyncio兼容库)。

相关问题 更多 >