jeudi 29 mars 2018

Unisex Bathroom: Threading in Python

Problem:

Males and Females can use a unisex bathroom.

The bathroom has infinite capacity but Males and Females cannot use the bathroom at the same time.

I'm having trouble enforcing the sex-segregation provision. I'm trying to use the LightSwitch design pattern from "Little Book of Semaphores" and I can't figure out why the pattern isn't excluding the opposite sex when a male or female is in the bathroom.

This isn't a homework problem. It's a problem I bombed on in an interview.

Users.py:

class User(object):

def __init__(self, cv, sex, line_full_event):
    self.cv = cv
    self.sex = sex
    self.line_full_event = line_full_event

def enter_bathroom(self, bathroom):
    with self.cv:
        bathroom.enter(self.sex, self.cv)

def leave_bathroom(self, bathroom):
    bathroom.leave(self.sex, self.cv)

class Male(User):

    def __init__(self, cv, sex, line_full_event):
        super(Male, self).__init__(cv, sex, line_full_event)

    def go(self, bathroom):
        logging.debug("Male queueing up")
        self.line_full_event.wait()
        super(Male, self).enter_bathroom(bathroom)
        super(Male, self).leave_bathroom(bathroom)

class Female(User):

   def __init__(self, cv, sex, line_full_event):
       super(Female, self).__init__(cv, sex, line_full_event)

   def go(self, bathroom):
       logging.debug("Female queueing up")
       self.line_full_event.wait()
       super(Female, self).enter_bathroom(bathroom)
       super(Female, self).leave_bathroom(bathroom)

LightSwitch.py:

class LightSwitch:

    def __init__(self):
        self.mutex = Lock()
        self.count = 0

    def inc(self, cv):
        with self.mutex:
            self.count += 1
            logging.debug("inc-ing! count == %d", self.count)
            if self.count == 1:
                cv.acquire()


    def dec(self, cv):
        with self.mutex:
            self.count -= 1
            logging.debug("dec-ing! count == %d", self.count)
            if self.count == 0:
                cv.notify_all()
                cv.release()

Bathroom.py

MALE = 1
FEMALE = 0

class Bathroom:

    def __init__(self):
        self.male_switch = LightSwitch()
        self.female_switch = LightSwitch()

    def enter(self, sex,  cv):
        if sex == MALE:
            self.female_switch.inc(cv)
        elif sex == FEMALE:
            self.male_switch.inc(cv)

    def leave(self, sex, cv):
        if sex == MALE:
            self.female_switch.dec(cv)
        elif sex == FEMALE:
            self.male_switch.dec(cv)

Main.py:

def Main():
    logging.basicConfig(format='%(threadName)s, %(asctime)s, %(message)s', datefmt='%M:%S', level=logging.DEBUG)
    # create Bathroom                                                                                                
    b = Bathroom()
    # create whatever threading objects we need                                                                      
    males_can_enter, females_can_enter = get_cvs()
    line_full = threading.Event()
    for i in range(10):
        if random.randint(0,1) == MALE:
            # create Male user                                                                                       
            user =  Male(males_can_enter, MALE, line_full)
        else:
            # create Female user                                                                                     
            user = Female(females_can_enter, FEMALE, line_full)
        t = threading.Thread(target=user.go, args=(b,))
        t.start()
    logging.debug("we're off to the races!")
    line_full.set()

def get_cvs():
    return (threading.Condition(), threading.Condition())


if __name__ == '__main__':
    Main()

Aucun commentaire:

Enregistrer un commentaire