开发者

twisted threading with subprocess.Popen?

I'm trying to implement a service with Twisted that's fairly close to the "finger" tutorial found here: http://twistedmatrix.com/documents/current/core/howto/tutorial/intro.html

I've got a basic.LineListener waiting for a command and then executing it, then I have a client connecting and issuing commands. Trouble is that the command sometimes needs to execute something else and I'm using python's subprocess module for that. It's not just that calls to communicate() are hanging, that's a normal subprocess issue and I know how to get past it. It's that subprocess.Popen calls are hanging.

Here's the twisted server code:

from twisted.application import internet, service
from twisted.internet import protocol, reactor, defer, threads
from twisted.protocols import basic
import sys
import time
import subprocess

class MyProtocol(basic.LineReceiver):
    def lineReceived(self, line):
        self.go()
    def go(self):
        def writeResponse(message):
            self.transport.write(message + '\r\n')
            self.transport.loseConnection()
        threads.deferToThread(self.factory.action).addCallback(writeResponse)
    def connectionMade(self):
        self.lines = []

class ActionService(service.Service):
    def __init__(self, **kwargs):
        pass
        #self.users = kwargs

def action(self):
    print "launching subprocess"
    sys.stdout.flush()
    p = subprocess.Popen(["ls"], stderr=subprocess.PIPE, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
    print "launched subprocess, trying to communicate..."
    sys.stdout.flush()
    p.communicate()
    print "returning"
    sys.stdout.flush()
    return "%032d" % (0)

    def getActionFactory(self):
        f = protocol.ServerFactory()
        f.protocol = MyProtocol
        f.action = self.action
        return f

reactor.suggestThreadPoolSize(300)
application = service.Application('Action', uid=0, gid=0)
f = ActionService()
serviceCollection = service.IServiceCollection(application)
internet.TCPServer(31337,f.getActionFactory()
                   ).setServiceParent(serviceCollection)

...and here's some client code:

#!/usr/bin/python
import time
import threading
import socket

def connectAction(host):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, 31337))
    s.send("asdf\r\n")
    resp = s.recv(32)
    s.close()
    return resp

class sscceThread(threading.Thread):
    def __init__(self, host):
        self.host = host
        threading.Thread.__init__(self)
    def run(self):
        connectAction(self.host)

def main():
    threads = []
    for i in range(0, 1000):
        for j in range(0,5):
            t = sscceThread("localhost")
            t.start()
            threads.append(t)
开发者_如何学Go        for t in threads:
            t.join()
        print i
        time.sleep(1)
    #    print i

if __name__ == "__main__":
    main()

Start the service by running:

twistd -y sscce_twisted_service.py -l twistdLog; tail -f twistdLog

And run the client by running:

./sscce_twisted_client.py

You should see the client go for a couple of iterations (I've seen it go as many as 10) and then hang. The client code contains a 1-second sleep so that you can tell the difference between twisted log entries from each iteration and on the one that hangs you'll see something like this in the twisted log:

2009-12-22 11:18:47-0800 [MyProtocol,55,127.0.0.1] launching subprocess
2009-12-22 11:18:47-0800 [MyProtocol,56,127.0.0.1] launching subprocess
2009-12-22 11:18:47-0800 [MyProtocol,55,127.0.0.1] launched subprocess, trying to communicate...
2009-12-22 11:18:47-0800 [MyProtocol,57,127.0.0.1]  launching subprocess
2009-12-22 11:18:47-0800 [MyProtocol,58,127.0.0.1] launching subprocess
2009-12-22 11:18:47-0800 [MyProtocol,56,127.0.0.1] launched subprocess, trying to communicate...
2009-12-22 11:18:47-0800 [MyProtocol,55,127.0.0.1] returning
2009-12-22 11:18:47-0800 [MyProtocol,57,127.0.0.1]  launching subprocess 
2009-12-22 11:18:47-0800 [MyProtocol,56,127.0.0.1]  launching subprocess returning
2009-12-22 11:18:47-0800 [MyProtocol,59,127.0.0.1]  launching subprocess
2009-12-22 11:18:47-0800 [MyProtocol,58,127.0.0.1]  launched subprocess, trying to communicate...
2009-12-22 11:18:47-0800 [MyProtocol,58,127.0.0.1] returning
2009-12-22 11:18:47-0800 [MyProtocol,59,127.0.0.1] launched subprocess, trying to communicate...
2009-12-22 11:18:47-0800 [MyProtocol,59,127.0.0.1] returning

Of particular note is MyProtocol,57. It says it was about to try launching the subprocess but it never printed the "launched subprocess, trying to communicate" line. I think it must have hung there.


As mg said in his comment, don't use the subprocess module. On POSIX platforms, it's necessary (more or less) to handle the SIGCHLD signal to deal with child processes that exit. Since there can only be one SIGCHLD handler, multiple libraries generally won't cooperate. Twisted's child process support and the subprocess module's support conflict. Either use Twisted's support (see http://twistedmatrix.com/documents/current/core/howto/process.html) or disable Twisted's support by passing installSignalHandlers=False to reactor.run (I recommend the former, as subprocess presents blocking interfaces which don't integrate well into Twisted-based applications).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜