QThreads et client XMLRPC
-
13-12-2019 - |
Question
J'essaie d'utiliser le client XMLRPC à partir de nombreuses QThreads.Pour vous assurer qu'un seul thread utilise le client XMLRPC, j'ai créé la serrure avec Qmutex.Mon code:
import sys
import xmlrpclib
import threading
import time
from SimpleXMLRPCServer import SimpleXMLRPCServer
from PyQt4 import QtCore, QtGui
class MM(object):
def __init__(self):
self.lock = QtCore.QMutex()
self.xmlrpc_client = xmlrpclib.ServerProxy('http://localhost:9092')
def __getattr__(self, name):
self.lock.lock()
sys.stderr.write('locked, for %s\n' % name)
print threading.current_thread()
result = self.xmlrpc_client.__getattr__(name)
sys.stderr.write('unlocked by %s\n' % name)
self.lock.unlock()
return result
class Server(QtCore.QThread):
def __init__(self):
QtCore.QThread.__init__(self)
self.server = None
def run(self):
self.server = SimpleXMLRPCServer(("localhost", 9092), logRequests = False)
def one():
return 1
self.server.register_function(one, 'one')
self.server.serve_forever()
print "SERVER DONE"
class Ask(QtCore.QThread):
def __init__(self, mmInst):
QtCore.QThread.__init__(self)
self.mm = mmInst
self._stopping = False
def run(self):
while not self._stopping:
time.sleep(0.5)
print self.mm.one()
def stop(self):
self._stopping = True
self.wait()
def start_gui():
app = QtGui.QApplication(sys.argv)
server = Server()
server.start()
time.sleep(1)
mm = MM()
print mm.one()
a1 = Ask(mm)
a1.start()
a2 = Ask(mm)
a2.start()
try:
app.exec_()
except KeyboardInterrupt:
server.server.shutdown()
if __name__ == "__main__":
start_gui()
Mais ça ne marche pas et je ne sais pas pourquoi.Voici la sortie:
locked, for one
<_MainThread(MainThread, started 1648)>
unlocked by one
1
locked, for one
<_DummyThread(Dummy-1, started daemon 356)>
unlocked by one
locked, for one
<_DummyThread(Dummy-2, started daemon 1480)>
unlocked by one
Traceback (most recent call last):
File "H:\poker\repos\TestSuite\test.py", line 46, in run
print self.mm.one()
File "C:\Python27\lib\xmlrpclib.py", line 1224, in __call__
return self.__send(self.__name, args)
File "C:\Python27\lib\xmlrpclib.py", line 1575, in __request
verbose=self.__verbose
File "C:\Python27\lib\xmlrpclib.py", line 1264, in request
return self.single_request(host, handler, request_body, verbose)
File "C:\Python27\lib\xmlrpclib.py", line 1289, in single_request
self.send_request(h, handler, request_body)
File "C:\Python27\lib\xmlrpclib.py", line 1391, in send_request
connection.putrequest("POST", handler, skip_accept_encoding=True)
File "C:\Python27\lib\httplib.py", line 853, in putrequest
raise CannotSendRequest()
httplib.CannotSendRequest
Traceback (most recent call last):
File "H:\poker\repos\TestSuite\test.py", line 46, in run
print self.mm.one()
File "C:\Python27\lib\xmlrpclib.py", line 1224, in __call__
return self.__send(self.__name, args)
File "C:\Python27\lib\xmlrpclib.py", line 1575, in __request
verbose=self.__verbose
File "C:\Python27\lib\xmlrpclib.py", line 1264, in request
return self.single_request(host, handler, request_body, verbose)
File "C:\Python27\lib\xmlrpclib.py", line 1294, in single_request
response = h.getresponse(buffering=True)
File "C:\Python27\lib\httplib.py", line 1015, in getresponse
raise ResponseNotReady()
httplib.ResponseNotReady
Lorsque vous utilisez un seul thread fonctionne bien:
$ diff -u test.py.back test.py
--- test.py.back 2012-03-14 01:34:37.666425000 +0100
+++ test.py 2012-03-14 01:33:01.423265000 +0100
@@ -63,8 +63,8 @@
a1 = Ask(mm)
a1.start()
- a2 = Ask(mm)
- a2.start()
+ #a2 = Ask(mm)
+ #a2.start()
try:
app.exec_()
$ python test.py
locked, for one
<_MainThread(MainThread, started -1219930432)>
unlocked by one
1
locked, for one
<_DummyThread(Dummy-1, started daemon -1287918736)>
unlocked by one
1
locked, for one
<_DummyThread(Dummy-1, started daemon -1287918736)>
unlocked by one
1
locked, for one
<_DummyThread(Dummy-1, started daemon -1287918736)>
unlocked by one
1
Lorsque vous utilisez deux instances différentes de MM fonctionnent également bien:
$ diff -u test.py.back test.py
--- test.py.back 2012-03-14 01:34:37.666425000 +0100
+++ test.py 2012-03-14 01:38:47.352862000 +0100
@@ -57,13 +57,13 @@
time.sleep(1)
- mm = MM()
- print mm.one()
+ #mm = MM()
+ #print mm.one()
- a1 = Ask(mm)
+ a1 = Ask(MM())
a1.start()
- a2 = Ask(mm)
+ a2 = Ask(MM())
a2.start()
try:
adam@sabayon /media/Nowy/poker/repos/TestSuite $ python test.py
locked, for one
locked, for one
<_DummyThread(Dummy-1, started daemon -1288275088)><_DummyThread(Dummy-2, started daemon -1298138256)>
unlocked by one
unlocked by one
11
locked, for one
<_DummyThread(Dummy-2, started daemon -1298138256)>
unlocked by one
locked, for one
<_DummyThread(Dummy-1, started daemon -1288275088)>
unlocked by one
1
1
locked, for one
<_DummyThread(Dummy-1, started daemon -1288275088)>
unlocked by one
locked, for one
1<_DummyThread(Dummy-2, started daemon -1298138256)>
unlocked by one
1
La solution
Il y avait quelques problèmes que j'ai vu avec le code, mais surtout celui que j'avais tout de suite que vous commencez à démarrer le client avant que le serveur n'était prêt. Lorsque j'ai d'abord instancié le serveur, alors le client, j'ai arrêté d'obtenir des erreurs de connexion.
J'ai également supprimé votre filage pendant la boucle et j'ai plutôt commencé la boucle d'événement Pyqt. De plus, vous n'abandonnez pas vraiment votre instance mm à votre classe de demande (pas que cela importait beaucoup pour cet exemple puisque vous utilisiez la même instance).
Quoi qu'il en soit, voici ma version qui semble fonctionner:
import sys
import xmlrpclib
import threading
import time
from SimpleXMLRPCServer import SimpleXMLRPCServer
from PyQt4 import QtCore, QtGui
class MM(object):
def __init__(self):
self.lock = QtCore.QMutex()
self.xmlrpc_client = xmlrpclib.ServerProxy('http://localhost:9092')
def __getattr__(self, name):
self.lock.lock()
sys.stderr.write('locked, for %s\n' % name)
print threading.current_thread()
result = self.xmlrpc_client.__getattr__(name)
sys.stderr.write('unlocked by %s\n' % name)
self.lock.unlock()
return result
class Server(QtCore.QThread):
def __init__(self):
QtCore.QThread.__init__(self)
self.server = None
def run(self):
self.server = SimpleXMLRPCServer(("localhost", 9092), logRequests = False)
def one():
return 1
self.server.register_function(one, 'one')
self.server.serve_forever()
print "SERVER DONE"
class Ask(QtCore.QThread):
def __init__(self, mmInst):
QtCore.QThread.__init__(self)
self.mm = mmInst
self._stopping = False
def run(self):
while not self._stopping:
time.sleep(0.5)
print self.mm.one()
def stop(self):
self._stopping = True
self.wait()
def start_gui():
app = QtGui.QApplication(sys.argv)
server = Server()
server.start()
time.sleep(.25)
mm = MM()
print mm.one()
a1 = Ask(mm)
a1.start()
a2 = Ask(mm)
a2.start()
try:
app.exec_()
except KeyboardInterrupt:
server.server.shutdown()
if __name__ == "__main__":
start_gui()
Mise à jour
Après avoir examiné cela un peu plus, j'ai réalisé que c'est un bogue dans Python 2.7 et XMLRPC, où ils ont changé comment cela crée des connexions. http://bugs.python.org/issue6907
Étrangement, ce code ne plante pas pour moi sur Python 2.6 / 2.7 sur OSX ou sur Python 2.6 sous Linux. Mais cela me bloque avec Python 2.7 sous Linux.
Lorsque j'ai déplacé le mécanisme de verrouillage en dehors de l'instance MM, il semblait commencer à travailler sous 2,7 sous Linux:
class MM(object):
def __init__(self):
self.xmlrpc_client = xmlrpclib.ServerProxy('http://localhost:9093')
def __getattr__(self, name):
return self.xmlrpc_client.__getattr__(name)
class Ask(QtCore.QThread):
def __init__(self, mmInst, lock):
QtCore.QThread.__init__(self)
self.mm = mmInst
self.lock = lock
def run(self):
while not self._stopping:
time.sleep(0.5)
self.lock.lock()
print self.mm.one()
self.lock.unlock()
def start_gui():
app = QtGui.QApplication(sys.argv)
...
lock = QtCore.QMutex()
a1 = Ask(mm, lock)
a1.start()
a2 = Ask(mm, lock)
a2.start()