如何在Twisted Klein中异步执行代码?

时间:2016-08-02 20:18:16

标签: python web-services asynchronous twisted klein-mvc

我的python Twisted Klein Web服务中有两个函数:

@inlineCallbacks
def logging(data):
    ofile = open("file", "w")
    ofile.write(data)
    yield os.system("command to upload the written file")

@APP.route('/dostuff')
@inlineCallbacks
def dostuff():
    yield logging(data)
    print "check!" 
    returnValue("42")

os.system("command to upload the written file")运行时,它会显示消息说"开始上传"然后"上传完成"。我想使日志记录函数异步,以便在logging处理程序打印出来之后dostuff处理程序中的处理发生" check!"。 (我实际上希望在returnValue(" 42")之后进行处理,但是我认为这两者都使得记录功能异步?)

我认为yield语句会使它无阻塞,但似乎并非如此,"检查!"总是在&#34之后打印;开始上传"和#34;上传完成"。我很感激是否有人可以给我一些反馈意见,因为我是异步编码的新手并且暂时被阻止了...

2 个答案:

答案 0 :(得分:3)

要使代码异步,您需要将Twisted Deferreds用作described here。 Deferreds为您提供了异步代码执行的API,它们允许您将回调附加到您的函数,并且它们在reactor对象管理的Twisted事件循环中执行代码。

我认为在你的案例中有两种可能的方法来使用Deferreds。

1)使用reactor.callLater()

在后台执行任务

如果dostuff处理程序不关心结果,这是可以的。您可以使用reactor.callLater()。这样,从doStuff返回值后,您的异步函数将执行。

这样的事情:

from klein import run, route, Klein
from twisted.internet import defer, task, reactor
import os

app = Klein()


def logging(data):
    ofile = open("file", "w")
    ofile.write(data)
    result = os.system("ls")
    print(result)


@route('/')
def dostuff(request):
    reactor.callLater(0, logging, "some data")
    print("check!")
    return b'Hello, world!'

run("localhost", 8080)

使用此代码的事件顺序如下,首先打印“check”,然后返回“hello world”响应,最后异步调用超过并打印运行os.system()的结果。

2016-08-11 08:52:33+0200 [-] check!
2016-08-11 08:52:33+0200 [-] "127.0.0.1" - - [11/Aug/2016:06:52:32 +0000] "GET / HTTP/1.1" 200 13 "-" "curl/7.35.0"
a.py  file

2)在后台执行任务并使用task.deferLater()

获取结果

如果您关心“日志记录”功能的结果,您还可以将回调附加到此对象并使用twisted.internet.task API。如果你想这样做,你需要重构你的处理程序才能像这样工作

@route('/')
def dostuff(request):
    def the_end(result):
        print("executed at the end with result: {}".format(result))
    dfd = task.deferLater(reactor, 0, logging, "some data")
    dfd.addCallback(the_end)
    print("check!")
    return b'Hello, world!'

这种方式的事件顺序与上面相同,但the_end函数将在logging函数完成后执行。

2016-08-11 08:59:24+0200 [-] check!
2016-08-11 08:59:24+0200 [-] "127.0.0.1" - - [11/Aug/2016:06:59:23 +0000] "GET / HTTP/1.1" 200 13 "-" "curl/7.35.0"
a.py  file
2016-08-11 08:59:24+0200 [-] executed at the end with result: some result

答案 1 :(得分:1)

'收益率'声明并不能让事情异步发生。它只是推迟包含它的函数的执行,并返回一个生成器对象,以后可以用来迭代序列。

所以dostuff()将返回一个生成器对象。在生成器对象稍后迭代之前不会发生任何事情。但是你的代码中没有任何东西可以实现这一点。我希望你的dostuff例程会产生语法错误,因为它包含yield和非空返回。记录例程不会做任何事情,因为它包含一个yield,它返回的生成器从未使用过。

最后,日志记录例程将在每次调用时截断其输出文件,因为它以模式' w'打开日志文件。每次调用。

对于异步执行,您需要某种形式的多处理。但我不认为在这种情况下需要这样做。您的记录功能相当轻,应该快速运行,不会影响dostuff的工作。

我建议尝试这样的事情:

@inlineCallbacks
def logging(data):
    try:
        logging._ofile.write(data + '\n')
    except AttributeError:
        logging._ofile = open("file", 'w')
        logging._ofile.write(data + '\n')

@APP.route('/dostuff')
@inlineCallbacks
def dostuff():
    logging("before!")
    os.system("command to upload the written file")
    logging("after!")
    return("42")

这里我们只打开一次日志记录文件,当_ofile未定义为日志记录属性时,第一次调用日志记录。在后续调用中,logging._ofile已经打开,try块中的write语句将成功。

例程dostuff()调用日志记录以指示我们即将完成工作,实际完成工作,然后调用日志记录以指示工作已完成,最后返回所需的值。