7

我的 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(我实际上希望在returnValue(“42”)之后进行处理,但是我认为这两个都使日志记录函数异步?)

我认为 yield 语句会使其非阻塞,但似乎并非如此,“检查!” 总是在“开始上传”和“上传完成”后打印。如果有人能给我一些反馈,我将不胜感激,因为我是异步编码的新手,并且被阻止了一段时间......

4

2 回答 2

4

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

在您的情况下,我看到了两种使用 Deferreds 的潜在方法。

1)在后台执行任务reactor.callLater()

如果dostuff处理程序不关心结果,这没关系。您可以使用reactor.callLater()。这样,您的 async 函数将在您从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”响应,最后异步调用成功并打印 running 的结果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
于 2016-08-11T07:00:31.287 回答
1

'yield' 语句不会使事情异步发生。它只是延迟执行包含它的函数并返回一个生成器对象,该对象稍后可用于迭代序列。

所以 dostuff() 将返回一个生成器对象。直到稍后某个生成器对象被迭代,什么都不会发生。但是您的代码中没有任何内容可以实现这一点。我希望您的 dostuff 例程会产生语法错误,因为它同时包含收益和非空返回。日志记录例程不会做任何事情,因为它包含一个 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")

这里我们只打开一次 logging 文件,第一次 logging 是在 _ofile 没有定义为 logging 的属性时调用的。在后续调用中,logging._ofile 将已经打开,并且 try 块中的 write 语句将成功。

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

于 2016-08-09T08:55:23.150 回答