[Twisted-Python] pb: callRemotes execute serially on server-side

Jean-Paul Calderone exarkun at divmod.com
Mon Jan 19 15:36:11 EST 2009


On Mon, 19 Jan 2009 13:42:14 -0500, David Karnowski <dkarnows at gmail.com> wrote:
>Hi all,
>
>I'm having a client-side pb app call the same server-side function 10
>times with this client-side code:
>
> def process_test(rootObject):
>     for i in range(10):
>         _logger.debug("Calling remote with arg: %d" % (i))
>         deferred = rootObject.callRemote("test_it", i)
>         deferred.addCallbacks(success, problem,
>callbackKeywords={'i': i}, errbackKeywords={'i': i})
>
>The server-side "remote_test_it" function takes 5 seconds to complete
>(per call). I find the following. The first point is what I'd expect,
>but points 2 & 3 are not:
>
>1) The above for-loop completes asynchronously, with no blocking. It's
>complete in a few-thousand milliseconds.
>2) The server-side executes serially. The second client-side call to
>"remote_test_it" doesn't start executing "test_it" on the server-side
>until after the first call has finished. I'd have thought these 10
>calls would occur in parallel (in separate server-side threads). No?
>3) None of the callbacks you see in the above client-side for-loop get
>executed until ALL 10 callRemotes have finished on the server-side.
>e.g. the callRemote for the first loop iteration finishes on the
>server-side in 5 seconds, but the associated client-side callback
>isn't executed until after 50 seconds (after the 10th loop iteration's
>server-side call finishes).
>
>I'm just starting with Twisted. Are points 2 & 3 expected, and is
>there a way for me to have them run in parallel rather than serially?

Twisted is based on "cooperative multitasking" - not preemptive
multithreading.  If you have a task which will take ten seconds
to complete, then you have several options:

  * Just let it take ten seconds.  Since there is no preemption, no
    events will be serviced until the task has finished (ie, for 10
    seconds).  This means you will have serial processing, rather
    than parallel.  This is what you're seeing now.

  * Bring preemptive multithreading into the picture.  Explicitly run
    the task in a thread.  This is probably what you were expecting to
    happen automatically, but it doesn't.  Fortunately, it's quite easy
    to do this - see twisted.internet.threads.deferToThread for the most
    basic (and usually adequate) solution.  You will end up with a Deferred
    which PB knows how to interact with (just return it from your remote_
    method).

  * Convert the implementation of your 10 second task into an asynchronous
    version without using preemptive multithreading.  If your task is CPU
    bound, this may not be suitable (but you might want to consider running
    it in another process rather than another thread, depending on what
    hardware you have available and other concerns).  However, if you're
    waiting for 10 seconds for an HTTP request to complete, then you should
    use some Twisted facility to do the slow network I/O asynchronously
    than blocking on it.  Depending on the network protocol being used, you
    may find a nice high-level asynchronous API somewhere in Twisted, or you
    may need to implement something yourself.

Hope this helps,

Jean-Paul




More information about the Twisted-Python mailing list