витая нить с подпроцессом. Открыть? - PullRequest
3 голосов
/ 22 декабря 2009

Я пытаюсь реализовать сервис с Twisted, который довольно близок к учебнику "finger", найденному здесь: http://twistedmatrix.com/documents/current/core/howto/tutorial/intro.html

У меня есть basic.LineListener, ожидающий команду и затем выполняющий ее, затем у меня есть клиент, подключающийся и выдающий команды. Проблема в том, что команде иногда нужно выполнить что-то еще, и я использую для этого модуль подпроцесса python. Это не просто вызовы Communication () зависают, это обычная проблема подпроцесса, и я знаю, как ее обойти. Это тот подпроцесс. Открытые вызовы зависают.

Вот код витой сервера:

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)

... и вот код клиента:

#!/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)
        for t in threads:
            t.join()
        print i
        time.sleep(1)
    #    print i

if __name__ == "__main__":
    main()

Запустите службу, запустив:

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

И запустите клиент, запустив:

./sscce_twisted_client.py

Вы должны увидеть, как клиент проходит несколько итераций (я видел, что это целых 10), а затем зависает. Клиентский код содержит 1-секундный режим ожидания, так что вы можете определить разницу между записями в витой логи от каждой итерации и на той, которая зависает, в витой логи вы увидите нечто подобное:

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

Особо следует отметить MyProtocol, 57. В нем говорится, что он собирался запустить подпроцесс, но в нем никогда не выводилась строка «запущенный подпроцесс, пытающийся связаться». Я думаю, что он там висел.

1 Ответ

6 голосов
/ 23 декабря 2009

Как сказал mg в своем комментарии, не используйте модуль подпроцесса. На платформах POSIX необходимо (более или менее) обрабатывать сигнал SIGCHLD для обработки дочерних процессов, которые завершаются. Поскольку может быть только один обработчик SIGCHLD, несколько библиотек обычно не будут взаимодействовать. Поддержка дочерних процессов в Twisted и конфликт поддержки модуля подпроцесса. Или используйте поддержку Twisted (см. http://twistedmatrix.com/documents/current/core/howto/process.html) или отключите поддержку Twisted, передав installSignalHandlers=False в reactor.run (я рекомендую первый, поскольку subprocess представляет блокирующие интерфейсы, которые плохо интегрируются в приложения на основе Twisted) .

...