扭曲:“defer.execute”和“threads.deferToThread”之间的区别

发布于 2024-09-18 19:05:57 字数 384 浏览 4 评论 0原文

在twisted中,defer.execute()threads.deferToThread()有什么区别?两者都采用相同的参数 - 一个函数和调用它的参数 - 并返回一个 deferred ,它将随着调用函数的结果而被触发。

threads 版本明确指出它将在线程中运行。但是,如果 defer 版本没有,那么调用它还有什么意义呢?在反应器中运行的代码永远不应该阻塞,因此它调用的任何函数都必须不阻塞。此时,您可以使用 defer.succeed(f(*args, **kwargs)) 而不是 defer.execute(f, args, kwargs) 来执行相同的结果。

What is the difference between defer.execute() and threads.deferToThread() in twisted? Both take the same arguments - a function, and parameters to call it with - and return a deferred which will be fired with the result of calling the function.

The threads version explicitly states that it will be run in a thread. However, if the defer version doesn't, then what would ever be the point of calling it? Code that runs in the reactor should never block, so any function it calls would have to not block. At that point, you could just do defer.succeed(f(*args, **kwargs)) instead of defer.execute(f, args, kwargs) with the same results.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

淡莣 2024-09-25 19:05:58

defer.execute 确实在同一线程中以阻塞方式执行该函数,并且您是正确的,因为 defer.execute(f, args, kwargs) 与 defer.succeed 的作用相同(f(*args, **kwargs)) 除了,如果函数 f,defer.execute 将返回一个已触发 errback 的回调 抛出异常。同时,在您的 defer.succeed 示例中,如果函数抛出异常,它将向外传播,这可能是不希望的。

为了便于理解,我将 defer.execute 的源代码粘贴在这里:

def execute(callable, *args, **kw):
    """Create a deferred from a callable and arguments.

    Call the given function with the given arguments.  Return a deferred which
    has been fired with its callback as the result of that invocation or its
    errback with a Failure for the exception thrown.
    """
    try:
        result = callable(*args, **kw)
    except:
        return fail()
    else:
        return succeed(result)

换句话说,defer.execute 只是将阻塞函数的结果作为延迟的快捷方式,然后您可以添加它回调/错误返回。回调将使用正常的链接语义来触发。这看起来有点疯狂,但 Deferreds 可以在添加回调之前“触发”,并且回调仍然会被调用。


因此,回答您的问题,为什么这有用?好吧,defer.execute 对于测试/模拟以及简单地将异步 api 与同步代码集成都很有用。

同样有用的是 defer.maybeDeferred ,它调用函数,然后如果函数已经返回 deferred 则简单地返回它,否则函数类似于 defer.execute 。当您编写一个需要可调用的 API 并在调用时为您提供延迟,并且您也希望能够接受正常的阻塞函数时,这非常有用。

例如,假设您有一个应用程序可以获取页面并使用它执行操作。而且,出于某种原因,您需要针对特定​​用例以同步方式运行它,例如在单次 crontab 脚本中,或响应 WSGI 应用程序中的请求,但仍保留相同的代码库。如果您的代码如下所示,则可以这样做:

from twisted.internet import defer
from twisted.web.client import getPage

def process_feed(url, getter=getPage):
    d = defer.maybeDeferred(getter, url)
    d.addCallback(_process_feed)

def _process_feed(result):
    pass # do something with result here

要在没有反应器的同步上下文中运行此代码,您只需传递一个备用​​ getter 函数,如下所示:

from urllib2 import urlopen

def synchronous_getter(url):
    resp = urlopen(url)
    result = resp.read()
    resp.close()
    return result

defer.execute does indeed execute the function in a blocking manner, in the same thread and you are correct in that defer.execute(f, args, kwargs) does the same as defer.succeed(f(*args, **kwargs)) except that defer.execute will return a callback that has had the errback fired if function f throws an exception. Meanwhile, in your defer.succeed example, if the function threw an exception, it would propagate outwards, which may not be desired.

For ease of understanding, I'll just paste the source of defer.execute here:

def execute(callable, *args, **kw):
    """Create a deferred from a callable and arguments.

    Call the given function with the given arguments.  Return a deferred which
    has been fired with its callback as the result of that invocation or its
    errback with a Failure for the exception thrown.
    """
    try:
        result = callable(*args, **kw)
    except:
        return fail()
    else:
        return succeed(result)

In other words, defer.execute is just a shortcut to take a blocking function's result as a deferred which you can then add callbacks/errbacks to. The callbacks will be fired with normal chaining semantics. It seems a bit crazy, but Deferreds can 'fire' before you add callbacks and the callbacks will still be called.


So to answer your question, why is this useful? Well, defer.execute is useful both for testing / mocking as well as simply integrating an async api with synchronous code.

Also useful is defer.maybeDeferred which calls the function and then if the function already returns a deferred simply returns it, else functions similar to defer.execute. This is useful for when you write an API which expects a callable that when called gives you a deferred, and you want to be able to accept normal blocking functions as well.

For example, say you had an application which fetched pages and did things with it. And, for some reason, you needed to run this in a synchronous fashion for a specific use case, like in a single-shot crontab script, or in response to a request in a WSGI application, but still keep the same codebase. If your code looked like this, it could be done:

from twisted.internet import defer
from twisted.web.client import getPage

def process_feed(url, getter=getPage):
    d = defer.maybeDeferred(getter, url)
    d.addCallback(_process_feed)

def _process_feed(result):
    pass # do something with result here

To run this in a synchronous context, without the reactor, you could just pass an alternate getter function, like so:

from urllib2 import urlopen

def synchronous_getter(url):
    resp = urlopen(url)
    result = resp.read()
    resp.close()
    return result
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文