HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Invoke only by a single thread

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
invokethreadsingleonly

Problem

I wrote a simple class in Python, which controls code invocation, in a multi-threaded environment, with the following logic:

The class' main method, named try_to_do, takes two function pointers as arguments: yes_we_can_fn and no_we_cannot_fn.

In any point in time, only a single thread can invoke code the function passed as yes_we_can_fn argument.

If a specific thread tries to invoke its code, but some other thread is already invoking its code, then the no_we_cannot_fn is invoked instead of yes_we_can_fn.

If there's an exception in the code being executed, it should raise to the calling context.

The code:

from threading import Lock

class MyController():
    def __init__(self):
        self.locker = Lock()
        self.is_active = False

    def try_to_do(self, yes_we_can_fn, no_we_cannot_fn):
        with self.locker:
            if self.is_active:
                we_can_do_it = False
            else:
                we_can_do_it = True
                self.is_active = True

        try:
            if we_can_do_it:
                yes_we_can_fn()
            else:
                no_we_cannot_fn()
        finally:
            if we_can_do_it:
                with self.locker:
                    self.is_active = False


Usage:

ctl = MyController()

def p1():
    from time import sleep
    print 'before sleep'
    sleep(2)
    print 'done'

def p2():
    print 'too busy, will try some other time'

ctl.try_to_do(p1, p2)


I'd like to get some reviews: thread safety (maybe I'm missing something?), coding style, etc.

Solution

You could avoid the is_active variable by using the lock in a non-blocking manner:

def try_to_do(self, yes_we_can_fn, no_we_cannot_fn):
    if self.locker.acquire(False):
        try:
            yes_we_can_fn()
        finally:
            self.locker.release()
    else:
        no_we_cannot_fn()

Code Snippets

def try_to_do(self, yes_we_can_fn, no_we_cannot_fn):
    if self.locker.acquire(False):
        try:
            yes_we_can_fn()
        finally:
            self.locker.release()
    else:
        no_we_cannot_fn()

Context

StackExchange Code Review Q#39647, answer score: 2

Revisions (0)

No revisions yet.