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

How to execute code asynchronously in Twisted Klein?

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

@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") 运行时,它会显示消息说 "start uploading" 然后 "upload complete"。我想让日志记录函数异步,以便 logging 处理程序中的处理发生在 dostuff 处理程序打印出 "check!" 之后。 (我实际上希望在 returnValue("42") 之后进行处理,但我认为这两者都使日志记录函数异步?)

我认为 yield 语句会使它成为非阻塞的,但似乎并非如此,"check!" 总是在 "start uploading" 和 "upload complete" 之后打印。如果有人能给我一些反馈,我将不胜感激,因为我是异步编码的新手并且在这方面被封锁了一段时间...

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

所以 dostuff() 将 return 生成器对象。在稍后某个时间迭代该生成器对象之前,什么都不会发生。但是您的代码中没有任何内容可以实现这一点。我希望您的 dostuff 例程会产生语法错误,因为它同时包含 yield 和非空 return。记录例程不会做任何事情,因为它包含一个 yield 并且它 returns 的生成器从未被使用过。

最后,记录例程将在每次调用时 运行 整理其输出文件,因为它在每次调用时都以 '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是在_ofile没有定义为日志属性的情况下。在随后的调用中,logging._ofile 将已经打开并且 try 块中的写入语句将成功。

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

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

我看到在您的案例中有两种可能使用 Deferreds 的方法。

1) 使用reactor.callLater()

在后台执行任务

如果 dostuff 处理程序不关心结果,这没关系。您可以使用 reactor.callLater()。这样你的异步函数将在你 doStuff.

的 return 值之后执行

所以像这样:

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" 响应 returned,最后异步调用成功并打印结果 运行 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()

获取结果

如果您关心 'logging' 函数的结果,您还可以将回调附加到此对象并使用 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