我什么时候应该在常规线程上使用asyncio,为什么?它是否提供了性能提升?

时间:2015-07-21 14:20:59

标签: python multithreading python-asyncio

我对Python中的多线程有一个非常基本的理解,甚至对asyncio的基本理解。

我目前正在编写一个基于Curses的小程序(最终将使用完整的GUI,但这是另一个故事),它处理主线程中的UI和用户IO,然后有另外两个守护程序线程(每个使用他们自己的队列/ worker-method-that-things-to-a-queue):

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

所有三个线程同时持续运行,这引出了一些问题:

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

谢谢!

1 个答案:

答案 0 :(得分:20)

  

当工作线程的队列(或者更一般地说,任何线程的队列)为空时,是否应该停止直到有再次发生的事情,或者是否可以继续运行?当并发线程除了观察其队列之外没有做任何事情时,它们是否会占用大量的处理能力?

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

  

两个线程的队列应该组合在一起吗?由于观察者线程持续运行单个方法,我猜工作者线程只能从观察者线程放入的单个队列中提取任务。

如果所有观察者正在做的事情是从队列中拉出东西并立即将其放入另一个队列中,它被单个工作者消耗掉,这听起来像是不必要的开销 - 你也可以直接在工作人员中消费它。但是,如果情况确实如此,那么对我来说并不完全清楚 - 观察者是从队列中消费,还是仅仅将物品放入其中?如果 从队列中消费,谁会把东西放进去?

  

我认为这不重要,因为我不是多处理,但是这个设置是否受到Python的GIL(我认为仍然存在于3.4中)的影响?

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

  

观察者线程应该像这样连续运行吗?根据我的理解,请纠正我,如果我错了,asyncio应该用于基于事件的多线程,这似乎与我正在尝试做的相关。

很难说,因为我不知道“连续跑”的含义。它在做什么?如果它花费大部分时间在queue上睡觉或阻塞,那就没关系 - 这两件事都释放了GIL。如果它不断地进行实际工作,则需要GIL,因此会降低应用程序中其他线程的性能(假设它们正在尝试同时进行工作)。 asyncio专为受I / O限制的程序而设计,因此可以使用异步I / O在线程中运行。听起来您的程序可能非常适合这种情况,具体取决于worker正在做什么。

  

主线程基本上只是等待用户按一个键来访问菜单的不同部分。 这似乎是asyncio非常适合的情况,但是,我不确定。

任何你大部分等待I / O的程序都可能对asyncio有好处 - 但只有你能找到一个可以创建curses的库(或者你最终选择的其他GUI库)才能很好地运行用它。大多数GUI框架都带有自己的事件循环,这将与asyncio冲突。您需要使用一个库,它可以使GUI的事件循环与asyncio的事件循环很好地配合。您还需要确保可以找到应用程序使用的任何其他基于同步I / O的库的asyncio兼容版本(例如数据库驱动程序)。

也就是说,通过从基于线程的程序切换到基于asyncio的程序,您不太可能看到任何性能提升。它的表现可能大致相同。由于您只处理3个线程,因此它们之间的上下文切换开销并不是很重要,因此从单线程异步I / O方法切换不会产生很大的差异。 asyncio将帮助您避免线程同步复杂性(如果您的应用存在问题 - 目前尚不清楚),并且至少在理论上,如果您的应用可能需要批次,则可以更好地扩展线程,但似乎并非如此。我认为对你来说,这基本上取决于你喜欢编写哪种风格(假设你可以找到你需要的所有asyncio - 兼容的库)。