【问题标题】:Python - How can I make this code asynchronous?Python - 如何使此代码异步?
【发布时间】:2011-02-11 06:12:19
【问题描述】:

这里有一些代码可以说明我的问题:

def blocking1():
    while True:
        yield 'first blocking function example'

def blocking2():
    while True:
        yield 'second blocking function example'

for i in blocking1():
    print 'this will be shown'

for i in blocking2():
    print 'this will not be shown'

我有两个包含while True 循环的函数。这些将产生数据,然后我将记录在某个地方(很可能是一个 sqlite 数据库)。

我一直在玩线程并让它工作。但是,我真的不喜欢它......我想做的是让我的阻塞函数异步。比如:

def blocking1(callback):
    while True:
        callback('first blocking function example')

def blocking2(callback):
    while True:
        callback('second blocking function example')

def log(data):
    print data

blocking1(log)
blocking2(log)

如何在 Python 中实现这一点?我已经看到标准库带有 asyncore,并且这个游戏中的大牌是 Twisted,但这两个似乎都用于套接字 IO。

如何异步我的非套接字相关的阻塞函数?

【问题讨论】:

  • What I would like to do is make my blocking functions asynchronous 这没有任何意义。您要么希望函数阻塞,要么希望它异步。如果您希望它异步,请使用线程。我不明白问题出在哪里。
  • 我希望我的阻塞函数是非阻塞的
  • 关于这些函数的执行是如何交错的,有很多问题需要回答,不仅是彼此交错,还有你运行的所有后续代码。这就是操作系统的用途,以及为什么您通常希望将线程用于类似的事情。你能解释一下你不喜欢线程吗?这正是创建线程来解决的问题。
  • 我想我不喜欢它,因为我不习惯它。我今天一直在玩它,但仍然不太明白发生了什么。我比较习惯回调的方式

标签: python asynchronous twisted nonblocking event-driven


【解决方案1】:

阻塞函数是一个不返回但仍让您的进程处于空闲状态的函数 - 无法完成更多工作。

您要求我们将您的阻塞函数设为非阻塞。然而——除非你正在编写一个操作系统——没有任何阻塞函数。你可能有函数因为调用阻塞系统调用而阻塞,或者你可能有函数因为它们进行大量计算而“阻塞”。

如果不使底层系统调用非阻塞,就不可能使前一种类型的函数成为非阻塞。根据该系统调用是什么,如果不向程序添加事件循环,可能很难使其成为非阻塞;您不仅需要拨打电话并让它不阻塞,还必须拨打另一个电话以确定该调用的结果将传送到您可以关联的地方。

这个问题的答案是一个很长的 python 程序和很多关于不同操作系统接口及其工作原理的解释,但幸运的是我已经在另一个网站上写了这个答案;我称它为Twisted。如果您的特定任务已经是supported by a Twisted reactor,那么您很幸运。否则,只要您的任务映射到一些现有的操作系​​统概念,您就可以扩展一个反应器来支持它。实际上,这些机制中只有 2 种:所有明智的操作系统上的文件描述符,以及 Windows 上的 I/O 完成端口。

在另一种情况下,如果您的函数消耗大量 CPU,因此没有返回,它们并不是真正的阻塞;您的流程仍在进行中并完成工作。解决方法有以下三种:

  • 单独的线程
  • 单独的进程
  • 如果您有一个事件循环,请编写一个定期产生的任务,方法是编写任务使其能够完成某些工作,然后要求事件循环在不久的将来恢复它,以允许其他任务执行运行。

在 Twisted 中,最后一种技术可以通过多种方式实现,但这里有一个语法上很方便的技巧,可以让它变得简单:

from twisted.internet import reactor
from twisted.internet.task import deferLater
from twisted.internet.defer import inlineCallbacks, returnValue

@inlineCallbacks
def slowButSteady():
    result = SomeResult()
    for something in somethingElse:
        result.workHardForAMoment(something)
        yield deferLater(reactor, 0, lambda : None)
    returnValue(result)

【讨论】:

  • 很棒的帖子。过去几个小时一直在网上搜索有关另一个 Python 套接字阻塞问题的信息,虽然这篇文章涉及不同的上下文(我的是 gevent/greenlets),但它是一个很好的初学者概述,说明为什么例程可能会在设置中阻塞Python。感谢发帖。
【解决方案2】:

您可以使用生成器进行协作多任务处理,但您必须编写自己的主循环来在它们之间传递控制。

这是一个(非常简单的)示例,使用您上面的示例:

def blocking1():
    while True:
        yield 'first blocking function example'

def blocking2():
    while True:
        yield 'second blocking function example'


tasks = [blocking1(), blocking2()]

# Repeat until all tasks have stopped
while tasks:
    # Iterate through all current tasks. Use
    # tasks[:] to copy the list because we
    # might mutate it.
    for t in tasks[:]:
        try:
            print t.next()
        except StopIteration:
            # If the generator stops, remove it from the task list
            tasks.remove(t)

您可以通过允许生成器产生新的生成器来进一步改进它,然后可以将其添加到任务中,但希望这个简化的示例能够提供总体思路。

【讨论】:

  • 这似乎是一个不错的解决方案。但是,如果两个生成器使用 libpcap 监听两个单独的接口并返回嗅探到的数据包,我会因为在给定时间只有一个生成器返回而丢失数据包吗?
  • @mike:是的。如果你打算做一些与 I/O 相关的事情,比如数据包嗅探,那么使用真正的踏板是一个更好的解决方案(除非有问题的 I/O 库提供了本机非阻塞 API)。
  • 这里的真正答案取决于具体情况。如果您正在阻塞 I/O 调用并且这就是问题所在,那么您需要转换 那些调用 以便它们不再阻塞。除非您拥有这些函数的事件驱动版本,否则您无法对其余代码执行任何操作以使其异步(无线程)。换句话说,请参阅基思的回答,并“使用扭曲”。
【解决方案3】:

扭曲的框架不仅仅是套接字。它具有适用于许多场景的异步适配器,包括与子流程交互。我建议仔细看看。它会做你想做的事。

【讨论】:

    【解决方案4】:

    如果您不想使用完整的操作系统线程,您可以尝试Stackless,它是 Python 的变体,增加了许多有趣的功能,包括“微线程”。有很多不错的examples 对您很有帮助。

    【讨论】:

    • 看起来很有趣,但我更愿意坚持使用 Python
    • 我将介绍文本解释为它是一个替代的 Python 解释器。我现在再看看它。干杯。
    • 它是一个替代的 Python 解释器。此外,它不会使您的任何代码非阻塞(即使使用微线程)。
    【解决方案5】:

    您的代码没有阻塞。 blocking1() 并且它的兄弟立即返回迭代器(不阻塞),单个迭代也不会阻塞(在你的情况下)。

    如果你想一个接一个地从两个迭代器中“吃掉”,在继续之前,不要让你的程序尝试完全吃掉“blocking1()”...

    for b1, b2 in zip(blocking1(), blocking2()):
        print 'this will be shown', b1, 'and this, too', b2
    

    【讨论】:

      猜你喜欢
      • 2018-10-09
      • 2018-09-06
      • 1970-01-01
      • 1970-01-01
      • 2019-04-14
      • 2018-06-08
      • 2014-03-12
      • 2019-01-16
      • 1970-01-01
      相关资源
      最近更新 更多