【问题标题】:Unisex Bathroom: Threading in Python中性浴室:Python 中的线程
【发布时间】:2018-03-29 17:07:23
【问题描述】:

问题:

男女均可使用男女通用的浴室。

卫生间容量无限,但男女不能同时使用卫生间。

我在执行性别隔离条款时遇到了麻烦。我正在尝试使用“信号量小书”中的 LightSwitch 设计模式,但我无法弄清楚为什么当男性或女性在浴室时该模式不排除异性。

这不是作业问题。这是我在一次采访中轰炸的问题。

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()

浴室.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()

输出:

Thread-1, 10:21, Male queueing up
Thread-2, 10:21, Female queueing up
Thread-3, 10:21, Male queueing up
Thread-4, 10:21, Female queueing up
Thread-5, 10:21, Male queueing up
Thread-6, 10:21, Female queueing up
Thread-7, 10:21, Male queueing up
Thread-8, 10:21, Female queueing up
Thread-9, 10:21, Female queueing up
Thread-10, 10:21, Male queueing up
MainThread, 10:21, we're off to the races!
Thread-2, 10:21, inc-ing! count == 1
Thread-3, 10:21, inc-ing! count == 1
Thread-3, 10:21, dec-ing! count == 0
Thread-2, 10:21, dec-ing! count == 0
Thread-5, 10:21, inc-ing! count == 1
Thread-9, 10:21, inc-ing! count == 1
Thread-5, 10:21, dec-ing! count == 0
Thread-9, 10:21, dec-ing! count == 0
Thread-4, 10:21, inc-ing! count == 1
Thread-7, 10:21, inc-ing! count == 1
Thread-4, 10:21, dec-ing! count == 0
Thread-7, 10:21, dec-ing! count == 0
Thread-8, 10:21, inc-ing! count == 1
Thread-10, 10:21, inc-ing! count == 1
Thread-8, 10:21, dec-ing! count == 0
Thread-10, 10:21, dec-ing! count == 0
Thread-1, 10:21, inc-ing! count == 1
Thread-6, 10:21, inc-ing! count == 1
Thread-1, 10:21, dec-ing! count == 0
Thread-6, 10:21, dec-ing! count == 0

【问题讨论】:

    标签: python multithreading design-patterns


    【解决方案1】:

    首先,我没有“Sempahores 小书”,30 秒的搜索没有产生任何与 CS 相关的“Lightswitch 设计模式”(但有很多关于家居装修的东西)。无论如何,我相信你的代码有点太复杂了。这是我想出的:

    import logging, threading, random, time
    
    # Lame enum in case you aren't on Python 3.4.
    # FEMALE = 0; MALE = 1
    names = ["Female", "Male"]
    
    class User(object):
        def __init__(self, sex):
            self.sex = sex
            self.name = names[sex]
    
        def go(self, bathroom):
            logging.debug("%s queueing up" % self.name)
    
            with bathroom.condition:
                while not bathroom.is_open(self):
                    bathroom.condition.wait()
    
            logging.debug("%s entering the bathroom" % self.name)
            bathroom.enter(self)
            time.sleep(1)
    
            logging.debug("%s leaving the bathroom" % self.name)
            bathroom.leave(self)
    
    class Bathroom(object):
        def __init__(self):
            self.condition = threading.Condition()
            self.current_sex = None
            self.count = 0
    
        def is_open(self, user):
            return self.current_sex is None or self.current_sex == user.sex
    
        def enter(self, user):
            assert(self.is_open(user))
            self.current_sex = user.sex
            self.count += 1
    
        def leave(self, user):
            assert(user.sex == self.current_sex)
            self.count -= 1
            assert(self.count >= 0)
    
            if self.count == 0:
                logging.debug("Bathroom is empty. Opening for anyone")
                self.current_sex = None
                with self.condition:
                    self.condition.notify_all()
    
    def Main():
        logging.basicConfig(format='%(threadName)s, %(asctime)s, %(message)s', datefmt='%M:%S', level=logging.DEBUG)
    
        b = Bathroom()
    
        logging.debug("we're off to the races!")
        for i in range(10):
            user = User(random.randint(0, 1))
            t = threading.Thread(target=user.go, args=(b,))
            logging.debug("Starting a thread")
            t.start()
    
    if __name__ == '__main__':
        Main()
    

    关于多线程部分的一些通用cmets:

    • 整个事情都基于一个threading.Condition,浴室会跟踪该threading.Condition
    • 任何人都可以在已指定性别或空着的浴室进入浴室。
    • 当浴室清空时,浴室会发出notify_all,让所有等待浴室可用的人。
    • 除了输出有意义之外,我还添加了一堆 asserts 以确保事情按预期工作。

    输出:

    MainThread, 19:27, we're off to the races!
    MainThread, 19:27, Starting a thread
    Thread-1, 19:27, Male queueing up
    Thread-1, 19:27, Male entering the bathroom
    MainThread, 19:27, Starting a thread
    Thread-2, 19:27, Female queueing up
    MainThread, 19:27, Starting a thread
    Thread-3, 19:27, Male queueing up
    MainThread, 19:27, Starting a thread
    Thread-3, 19:27, Male entering the bathroom
    Thread-4, 19:27, Female queueing up
    MainThread, 19:27, Starting a thread
    Thread-5, 19:27, Female queueing up
    MainThread, 19:27, Starting a thread
    Thread-6, 19:27, Female queueing up
    MainThread, 19:27, Starting a thread
    Thread-7, 19:27, Male queueing up
    MainThread, 19:27, Starting a thread
    Thread-7, 19:27, Male entering the bathroom
    Thread-8, 19:27, Female queueing up
    MainThread, 19:27, Starting a thread
    Thread-9, 19:27, Male queueing up
    MainThread, 19:27, Starting a thread
    Thread-9, 19:27, Male entering the bathroom
    Thread-10, 19:27, Male queueing up
    Thread-10, 19:27, Male entering the bathroom
    Thread-3, 19:28, Male leaving the bathroom
    Thread-1, 19:28, Male leaving the bathroom
    Thread-7, 19:28, Male leaving the bathroom
    Thread-10, 19:28, Male leaving the bathroom
    Thread-9, 19:28, Male leaving the bathroom
    Thread-9, 19:28, Bathroom is empty. Opening for anyone
    Thread-2, 19:28, Female entering the bathroom
    Thread-4, 19:28, Female entering the bathroom
    Thread-5, 19:28, Female entering the bathroom
    Thread-6, 19:28, Female entering the bathroom
    Thread-8, 19:28, Female entering the bathroom
    Thread-6, 19:29, Female leaving the bathroom
    Thread-5, 19:29, Female leaving the bathroom
    Thread-4, 19:29, Female leaving the bathroom
    Thread-2, 19:29, Female leaving the bathroom
    Thread-8, 19:29, Female leaving the bathroom
    Thread-8, 19:29, Bathroom is empty. Opening for anyone
    

    请注意,从一开始,单性中的每个人都是如何在浴室里暴动的(因为他们可以使用)。每个人都离开后,异性的每个人都围攻浴室。这会产生两波“人潮”(每波波需要一秒钟多一点的时间才能完成,因为这就是sleep 的持续时间。

    我还简化了您的一些代码,以使线程部分更清晰,并总体上改进关注点分离。值得注意的是,我完全删除了 line_full 事件,因为我认为其余代码没有必要按预期工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-19
      • 2016-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多