On 2014-01-07 02:29, Cameron Simpson wrote:
On 06Jan2014 18:56, Skip Montanaro <skip.montan...@gmail.com> wrote:
[...]
Let's say I have a dead simple GUI with two buttons labeled, "Do A" and "Do
B". Each corresponds to executing a particular activity, A or B, which take
some non-zero amount of time to complete (as perceived by the user) or
cancel (as perceived by the state of the running system - not safe to run A
until B is complete/canceled, and vice versa). The user, being the fickle
sort that he is, might change his mind while A is running, and decide to
execute B instead. (The roles can also be reversed.) If s/he wants to run
task A, task B must be canceled or allowed to complete before A can be
started.

I take it we can ignore user's hammering on buttons faster than
jobs can run or be cancelled?

Logically, the code looks something like (I fear Gmail is going to
destroy my indentation):

def do_A():
when B is complete, _do_A()
cancel_B()
[...]
def _do_A():
do the real A work here, we are guaranteed B is no longer running
[...]
cancel_A and cancel_B might be no-ops, in which case they need to start up
the other calculation immediately, if one is pending.

I wouldn't have cancel_A do this, I'd have do_A do this more overtly.

This is pretty simple execution, and if my job was this simple, I'd
probably just keep doing things the way I do now, which is basically to
catch a "complete" or "canceled" signal from the A and B tasks and execute
the opposite task if it's pending. But it's not this simple. In reality
there are lots of, "oh, you want to do X? You need to make sure A, B, and C
are not active." And other stuff like that.

What's wrong with variations on:

   from threading import Lock

   lock_A = Lock()
   lock_B = Lock()

   def do_A():
     with lock_B():
       with lock_A():
         _do_A()

   def do_B():
     with lock_A():
       with lock_B():
         _do_B()

It's safer to lock in the same order to reduce the chance of deadlock:

    def do_A():
      with lock_A():
        with lock_B():
          _do_A()

    def do_B():
      with lock_A():
        with lock_B():
          _do_B()

You can extend this with multiple locks for A,B,C provided you take
the excluding locks before taking the inner lock for the core task.

Regarding cancellation, I presume your code polls some cancellation
flag regularly during the task?


--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to