Archive for April 16th, 2008

在Twisted中使用线程

Wednesday, April 16th, 2008

在Twisted中使用线程

译者: gashero

目录

1   以线程安全的模式运行代码

Twisted中的大部分代码都不是线程安全的。例如protocol向transport写入数据就不是线程安全的。因此我们需要一种方法来在主事件循环中进行调度。者可以使用函数 twisted.internet.interfaces.IReactorThreads.callFromThread 来实现:

from twisted.internet import reactor

def notThreadSafe(x):
    """做一些非线程安全的事情"""
    # ...

def threadSafeScheduler():
    """以线程安全方式运行"""
    reactor.callFromThread(notThreadSafe,3) #将会运行notThreadSafe(3)在主时间循环中

Note

译者注

callFromThread 意指从线程调用,这个方法是供线程调用的,并且使其指定的函数加入到主事件循环中执行。比如worker线程可以调用此方法将提交结果的函数加入到主事件循环中。这样就可以确保多线程的运行worker,而有可以使用线程安全的方式提交结果。

2   在线程中运行代码

有时我们希望在线程中运行代码,比如阻塞的存取API。Twisted提供了这样做的方法在 IReactorThread API 中。附加的工具在包 twisted.internet.threads 中提供。这些方法允许我们把任务排队以后在线程池中运行。

例如,在线程中运行一个函数,我们可以:

from twisted.internet import reactor

def aSillyBlockingMethod(x):
    import time
    time.sleep(2)
    print x

# 在线程中运行
reactor.callInThread(aSillyBlockingMethod,"2 secodns have passed")

Note

译者注

callInThread 意指在线程中运行,调用该方法需要在主事件循环中,而执行其传入的函数则是在线程中。可以与上一节提供的 callFromThread`结合使用,即在worker线程函数中调用 `callFromThread 提交结果。

3   工具函数

工具函数作为 twisted.internet.reactor 的一部分API提供,但是并不是在 twisted.internet.threads 中实现的。

如果我们有多个方法需要在线程中以队列方式运行,我们可以做:

from twisted.internet import threads

def aSillyBlockingMethodOne(x):
    import time
    time.sleep(2)
    print x

def aSillyBlockingMethodTwo(x):
    print x

# 排队后在线程中运行两个方法
commands=[(aSillyBlockingMethodOne,["calling first"])]
commands.append((aSillyBlockingMethodTwo,["and the second"],{}))
threads.callMultipleInThread(commands)

如果我们希望得到函数的运行结果,那么我们可以使用Deferred:

from twisted.internet import threads

def doLongCalculation():
    # ... do long calculation here ...
    return 3

def printResult(x):
    print x

# 在线程中运行,并且通过 defer.Deferred 获取结果
d=threads.deferToThread(doLongCalculation)
d.addCallback(printResult)

如果你希望在reactor线程中调用一个方法,并且获取结果,你可以使用 blockingCallFromThread

from twisted.internet import threads,reactor,defer
from twisted.web.client import getPage
from twisted.web.error import Error

def inThread():
    try:
        result=threads.blockingCallFromThread(reactor,getPage,"http://twistedmatrix.com/")
    except Error,exc:
        print exc
    else:
        print result
    reactor.callFromThread(reactor.stop)

reactor.callInThread(inThread)
reactor.run()

blockingCallFromThread 将会返回对象或者抛出异常,或者通过抛出到传递给他的函数。如果传递给它的函数返回了一个Deferred,他会返回Deferred回调的值或者抛出异常到errback。

4   管理线程池

线程池是在 twisted.python.threadpool.ThreadPool 中实现的。

我们可以修改线程池的大小,增加或者减少可用线程的数量,可以这么做:

from twisted.internet import reactor
reactor.suggestThreadPoolSize(30)

缺省的线程池大小依赖于使用的reactor,缺省的reactor使用最小为5个,最大为10个。在你改变线程池尺寸之前,确保你理解了线程和他们的资源使用方式。

在Twisted2.5.0中使用线程

译者: gashero

刚才翻译了对应版本8.0.0的Twisted的线程指南,但是我还是在用2.5.0,所以这里只记录与8.0.0的差异,不做重新翻译。

当你开始使用线程之前,确保你在启动程序的时候使用了如下:

from twisted.python import threadable
threadable.init()

这回让Twisted以线程安全的方式初始化,不过仍然注意,Twisted的大部分仍然不是线程安全的。

以线程安全的方式运行代码中初始化多了两行:

from twisted.python import threadable
threadable.init(1)

2.5.0的文档中没有 blockingCallFromThread 的例子。也许根本就没有这个方法。

实际我下载文档的版本是2.4.0,不过应该与2.5.0一样的。