Let's say I have an object who's class definition looks like:
cl开发者_如何学Goass Command:
foo = 5
def run(self, bar):
time.sleep(1)
self.foo = bar
return self.foo
If this class is instantiated once, but different threads are hitting its run method (via an HTTP request, handled separately) passing in different args, what is the best method to queue them? Can this be done in the class definition itself?
I am using an XML RPC server (separate class). For simplicity's sake, we can say it has one instance of the Command class instantiated as a class variable. When Command.run() is being hit by two separate threads, how can I make sure that one run() method is complete before the next one is started?
I could do something like:
while self.busy:
time.sleep(1)
self.busy = true
...
self.busy = false
return self.foo
but that, as far as I know, would not give priority to the oldest request.
I realize how redundant this whole exercise sounds, since I could just run the XML-RPC server synchronously. But, to make a long story short, there are multiple Command instantiations and I do not want to block requests for one because another is busy.
I hope this makes more sense.
Thanks.
Here's a relatively simple approach (ignores exceptions, attribute-access, special methods, etc):
import Queue
import threading
def serialize(q):
"""runs a serializer on queue q: put [-1]*4 on q to terminate."""
while True:
# get output-queue for result, a callable, its args and kwds
out_q, tocall, args, kwds = q.get()
if out_q == -1:
return
result = tocall(*args, **kwds)
out_q.put(result)
class WrapCall(object):
"""Wraps a callable to serialize calls to it."""
def __init__(self, inq, ouq, tocall):
self.inq = inq
self.ouq = ouq
self.tocall = tocall
def __call__(self, *a, **k):
self.inq.put((self.ouq, self.tocall, a, k))
if self.ouq is None:
return None
return self.ouq.get()
class WrapObj(object):
"""Wraps any object to serialize all calls to its methods."""
def __init__(self, obj):
self._o = obj
self._q = Queue.Queue()
t = threading.Thread(target=serialize, args=(self._q,))
t.setDaemon(True)
t.start()
self.t = t
def __getattr__(self, n):
"""Wraps methods of self.w into an appropriate WrapCall instance."""
towrap = getattr(self._o, n)
if not callable(towrap):
raise TypeError('Cannot wrap noncallable attribute %r (type: %s)'
% (n, type(towrap)))
q = Queue.Queue()
return WrapCall(self._q, q, towrap)
def WrapperWait(self):
"""Return only when self.t has served all pending requests."""
q = Queue.Queue()
w = WrapCall(self.__q, q, lambda: None)
return w()
With this "serializer", you can do
myobj = WrapObj(Command())
and now all calls to myobj's (non-special) methods are serialized in a thread-safe way.
For your specific case, where there's only one method on the object, you could simplify this a bit further, but this is an already-simplified version of something I wrote and use often (supporting attribute getting and setting, special methods, etc, is a tad too complex to be worth it; the complete version does support catching and reraising exceptions raised in the wrapper object's methods, an optimization for calls whose results or exceptions you don't care about, and a few more tweaks, but not serialization of attributes and special methods).
That depends on how you plan to consume foo
. The simplest is to use Python's queue
module to synchronize the delivery of values to a consumer, but that assumes that the consumer wants to receive every value. You might have to be more specific to get a better answer.
精彩评论