1
\$\begingroup\$

I recently came across the Sleeping Barber problem and found out that it is a good place to start learning how to use threads properly.

I don't have too much experience with threads and want to learn more, so I figured I'd give it a go.

I'm hoping to get some constructive criticism on the code and any heads up on the logic of the problem if I'm not implementing it correctly.

import time
import random
import Queue
from threading import Thread

NAMES = ["Al", "Alex", "Anthony", "Bill", "Bob", "Brad", "Cam", "Cal",
         "Chris", "Charlie", "Dave", "Dan", "Derek", "Devin", "Eric",
         "Elijah", "Frank", "Fred", "Gary", "George", "Hal", "Harry",
         "Isaac", "Ishmael", "Jared", "Jake", "Jeremy", "Kevin", "Kris",
         "Larry", "Louie", "Mark", "Mort", "Nathan", "Norb", "Oscar",
         "Orville", "Peter", "Paul", "Quinn", "Rob", "Rick", "Steve",
         "Tim", "Trevor", "Ulysses", "Victor", "Walter", "Xavier",
         "Yadier", "Zack"]

class Barber(Thread):

    def __init__(self, waiting_customers):
        super(Barber, self).__init__()
        self.waiting_customers = waiting_customers # the Queue passed in
        self.number_haircuts = 0
        self.tips = 0 
        self.days_profit = 0 
        self.shop_open = True # Flag to end the main thread
        self.sleeping = True # Flag to have the barber start at sleep until a customer arrives

    def run(self):
        ''' 
           I check if the `shop_open` flag is True *and* there arent any customers in line.
           I did this to make sure that if a customer thread is put in the queue but, 
           isnt called before the `shop_open` is false, the barber thread will continue
           to get the last customers out but,  no other customers will be put in the 
           waiting room(no customer threads left alive after barber exits).
        '''
        while self.shop_open or not self.waiting_customers.empty(): # continue until Queue is empty or shop is closed 
            if not self.waiting_customers.empty(): 
                self.sleeping = False # if customers awake 
                customer = self.waiting_customers.get()
                if customer.hair_type == 'short':
                    customer.in_barber_chair = True
                    print "The barber is cutting %s's hair." % customer.name
                    print "%s has %s hair, this should take 20 minutes." % (customer.name, customer.hair_type)
                    time.sleep(2)
                elif customer.hair_type == 'medium':
                    customer.in_barber_chair = True
                    print "The barber is cutting %s's hair." % customer.name
                    print "%s has %s hair, this should take 40 minutes." % (customer.name, customer.hair_type)
                    time.sleep(4)
                else:
                    customer.in_barber_chair = True
                    print "The barber is cutting %s's hair." % customer.name
                    print "%s has %s hair, this should take 60 minutes." % (customer.name , customer.hair_type)
                    time.sleep(6)
                print "Done cutting %s's hair" % customer.name
                self.number_haircuts += 1
                self.tips += random.choice(range(6))
                self.days_profit += 15
            elif not self.sleeping: # elif no customers in line and not already sleeping go to sleep
                self.sleeping = True 
                print "No one in the waiting room, the barber is going to nap in his chair"
        print "The barber gave %d haircuts and made $%d." % (self.number_haircuts, 
                                                             sum([self.tips, self.days_profit]))  


class Customer(Thread):

    def __init__(self, name, hair_type):
        super(Customer, self).__init__()
        self.name = name
        self.hair_type = hair_type
        self.in_barber_chair = False

    def run(self):
        while not self.in_barber_chair:
            pass
        if self.hair_type == 'short': # have the thread stay alive while getting haircut.
            time.sleep(2)
        elif self.hair_type == 'medium': 
            time.sleep(4)
        else:
            time.sleep(6)


if __name__ == '__main__':
    hair_types = ['short', 'medium', 'long'] # additional variable for a haircut length to simulate time
    waiting_room = Queue.Queue(3) # a queue for a waiting room with max 3 chairs
    barber = Barber(waiting_room)        
    barber.start()
    close_time = 48 # end thread after 48 seconds
    shop_open = time.time()
    current_time = shop_open
    while (current_time - shop_open) < close_time: # check if barber thread has been running longer than open time
        i_need_a_haircut = random.choice(NAMES) # pick a random name
        if not waiting_room.full(): # if the Queue isn't full grab a seat
            length = random.choice(hair_types)
            customer = Customer(i_need_a_haircut, length)
            waiting_room.put(customer)
            print "%s sat down in the waiting room" % i_need_a_haircut
            customer.start()        
        else: # else leave shop
            print "Sorry, %s too full, try coming back when its not so busy" % i_need_a_haircut
        stagger = random.choice([1, 5, 10, 20]) # stagger time each customer thread is created
        time.sleep(stagger)
        current_time = time.time()
    barber.shop_open = False # close shop/barber will finish off remaining threads in queue.
\$\endgroup\$
  • \$\begingroup\$ Please add a description of the problem in your question. \$\endgroup\$ – SylvainD Jun 26 '13 at 21:17
  • \$\begingroup\$ @Josay I added a hyperlink on the "sleeping barber" text. \$\endgroup\$ – tijko Jun 26 '13 at 21:47
1
\$\begingroup\$

I have no idea what you are trying to achieve here so here are a full general comments :

The pythonic way to check not self.waiting_customers.empty() is self.waiting_customers (if waiting_customers is known to be a container).

Also

            if customer.hair_type == 'short':
                customer.in_barber_chair = True
                print "The barber is cutting %s's hair." % customer.name
                print "%s has %s hair, this should take 20 minutes." % (customer.name, customer.hair_type)
                time.sleep(2)
            elif customer.hair_type == 'medium':
                customer.in_barber_chair = True
                print "The barber is cutting %s's hair." % customer.name
                print "%s has %s hair, this should take 40 minutes." % (customer.name, customer.hair_type)
                time.sleep(4)
            else:
                customer.in_barber_chair = True
                print "%s has %s hair, this should take 60 minutes." % (customer.name , customer.hair_type)
                time.sleep(6)

could/should be written :

            duration = 2 if customer.hair_type == 'short' else 4 if customer.hair_type == 'medium' else 6
            customer.in_barber_chair = True
            print "The barber is cutting %s's hair." % customer.name
            print "%s has %s hair, this should take %0 minutes." % (customer.name, duration, customer.hair_type)
            time.sleep(duration)

Also, as you do this operation twice, it might be worth defining a function to convert hair types to duration.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I'll put in a link to the problem on wikipedia. Two good points, I had just starting using the Queue module and thought I would use the .empty() method. I did think at one point to make another method out of hair_type action too. Thanks for the response. \$\endgroup\$ – tijko Jun 26 '13 at 21:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.