Universidade Estadual de Ponta Grossa – Engenharia de Computação
Gustavo Marenda de Lima - RA: 19021026
Thiago Pankievicz – RA: 19011226
Professor: Dierone
Problema: Barbeiro Dorminhoco
Este problema consiste em manter o barbeiro ocupado enquanto há clientes no estabelecimento , e fazê-lo descansar quando o estabelecimento se encontrar vazio. No estabelecimento há uma cadeira de barbeiro e n cadeiras de espera. Quando não tem clientes, o barbeiro irá sentar na cadeira de barbeiro e ir dormir. Quando um cliente chega, o mesmo deve acordar o babeiro. No caso da chegada de mais clientes, eles irão esperar caso haja cadeiras de espera vazias, caso contrário irão sair do estabelecimento.

No gif a seguir temos um exemplo de quando o cliente entra na barbearia vazia, e acorda o barbeiro, fazendo com que o mesmo corte o seu cabelo.

Já neste exemplo, o barbeiro está acordado e ja está ocupado, com apenas uma cadeira de espera vazia. Dessa forma o cliente que entra na barbearia deve sentar em uma das cadeiras disponíveis e esperar.

Neste último exemplo, a barbearia está cheia, e os novos clientes que chegam para cortar o cabelo, acabam indo embora.

O problema é programar o barbeiro e os clientes sem cair em condições de disputa (condições de corrida) (race conditions). Condições de disputa são situações em que duas ou mais threads (ou processos) estão trabalhando juntas e podem compartilhar algum armazenamento comum que cada uma pode ler e gravar. O resultado final depende de quem executa precisamente quando. Os resultados da maioria dos programas são bons, mas, de vez em quando, acontece algo estranho e inconsistente. Esse problema é semelhante a situações com várias filas, como uma mesa de atendimento de telemarketing com diversos atendentes e com um sistema computadorizado de chamadas em espera, atendendo a um número limitado de chamadas que chegam. Uma solução usa três semáforos: customers, que conta os clientes à espera de atendimento (exceto o cliente que está na cadeira de barbeiro, que não está esperando); barbers, o número de barbeiros (0 ou 1) que estão ociosos à espera de clientes, e mutex, que é usado para exclusão mútua. Precisamos ainda de uma variável, waiting, que também conta os clientes à espera de atendimento. É essencialmente uma cópia de customers. A razão de se ter waiting é que não há uma maneira de ler o valor atual do semáforo. Nessa solução, um cliente que entra na barbearia deve contar o número de clientes à espera de atendimento. Se este for menor que o número de cadeiras, ele ficará; do contrário, ele sairá. Nesta solução, quando chega de manhã para trabalhar, o barbeiro executa o método barber, que o leva a bloquear sobre o semáforo customers, que inicialmente está em 0. O barbeiro então vai dormir, e permanece dormindo até que o primeiro cliente apareça. Quando chega, o cliente executa customer e inicia obtendo mutex para entrar em uma região crítica. Se um outro cliente chega logo em seguida, o segundo nada pode fazer até que o primeiro libere o mutex. O cliente então verifica se o número de clientes à espera é menor que o número de cadeiras. Se não for, ele liberará o mutex e sairá sem cortar o cabelo. Se houver uma cadeira disponível, o cliente incrementará a variável inteira waiting. Ele faz então um up no semáforo customers, que acorda o barbeiro. Nesse ponto, o cliente e o barbeiro estão ambos acordados. Quando o cliente libera mutex, o barbeiro o pega, faz alguma limpeza e começa a cortar o cabelo. Quando termina o corte de cabelo, o cliente sai do procedimento e deixa a barbearia. Diferente de nossos exemplos anteriores, não há um laço para o cliente porque para cada um deles terá apenas um corte de cabelo. O barbeiro, contudo, contém um laço para tentar obter o próximo cliente. Se houver algum outro cliente, um novo corte de cabelo será iniciado. Do contrário, o barbeiro cairá no sono.
Import threading
from time import sleep as sp
import random
#semáforos
customers = threading.semaphore(0) # mantém fila de clientes
barbers = threading.semaphore(0) # diz se o barbeiro está disponível ou não
mutex = threading.semaphore(1) # para exclusão mútua
#variáveis
sleep = 0 # diz se o barbeiro está dormindo ou não (1 - dormindo, 0 - acordado)
waiting = 0 #número de clientes esperando nos acentos
total = 0 # total de clientes presentes atualmente (incluindo quem está recebendo o corte)
waiting_list = [ ] # nome dos clientes esperando na fila
cust_getting_haircut = ‘ ’ # nome do cliente recebendo o corte
#funções
def barber ():
global total, waiting, sleep, waiting_list, cust_getting_haircut
while true:
print (“barbeiro esperando mais clientes, total de clientes
esperando:”, waiting, “\n”)
if (len(waiting_list) > 0): # se alguém estiver esperando
print (“clientes esperando são: ”, waiting_list)
if (waiting == 0 and total == 0): # se nenhum cliente estiver
presente, barbeiro dorme
print(“barbeiro dorme”)
sleep = 1
customers.acquire () # wait (customers)
mutex.acquire () # wait (mutex)
if (waiting > 0): # se o cliente estiver esperando
waiting -= 1 # decrementa número de clientes na fila
barbers.release () # signal (barber)
mutex.release() # signal (mutex)
sp (1) # espera por 1 segundo
cut_hair () # barbeiro vai cortar o cabelo
sp (4) # cortando por 4 segundos
print (f “barbeiro terminou o corte de {cust_getting_haircut}\n”)
if (len(wating_list) > 0 and cust_getting_haircut in waiting_list):
# remove o cliente
waiting_list.remove(cust_getting_haircut)
cust_getting_haircut = ‘ ’
total -= 1 # decrementa total
def customer(name):
global chairs, waiting, cust_getting_haircut, sleep, waiting_list, total
mutex.acquire() # wait (mutex)
if (waiting < chairs or total == 0):
if (total == 0):
total += 1 # incrementa total
print (f ‘cliente: {name} entrou na barbearia\n’)
else:
total += 1
waiting += 1 # incrementa número de clientes esperando
waiting_list.append(name) # adiciona nome do cliente na
lista de espera
print (f ‘cliente: {name} entrou na sala de espera, total
de clientes esperando :’, waiting, “\n”)
customers.release () # signal (customers)
mutex.release () # signal (mutex)
barbers.acquire () # wait (barber)
if (sleep == 1): # se o barbeiro estiver dormindo, ele acorda
print(f ‘cliente: {name} está acordando o barbeiro \n’)
sleep = 0 # barbeiro está acordado
cust_getting_haircut = name
get_hair_cut (name)
else: # se não houver mais acentos disponíveis
mutex.release () # signal (mutex)
balk (name) # deixar a loja
def get_hair_cut (name):
‘ ‘ ‘ Request for hair cut ’ ’ ’
print (f ‘Cliente: {name} chamou GET_HAIR_CUT\n’)
return
def cut_hair ():
‘ ‘ ‘ Cut the hair ’ ’ ’
print (f “Barbeiro cortando o cabelo de {cust_getting_haircut}\n”)
return
def balk (name):
‘ ‘ ‘ Leave the shop ’ ’ ’
print (f ‘Cliente: {name} está tentando entrar na sala de espera
\nloja cheia, chamada balk para {name} \n’)
#threads e outras variáveis
barber_thread = threading.Thread (name=“Barbeiro”, target=barber) # inicializa thread para o barbeiro
barber_thread.start() # começa a thread do barbeiro
custs = [ ‘Mr. a’, ‘Mr. b’, ‘Mr. c’, ‘Mr. d’, ‘Mr. e’, ‘Mr. f’, ‘Mr. g’, ‘Mr. h’ ]
cust_threads = [ ] # todas as threads de clientes
while true:
global chairs
if (len(cust_threads)): # se houverem threads em cust_threads
for t in cust_threads:
t.join () # executa os seguintes códigos somente quando
todas as threads de clientes terminam suas execuções
if (waiting == 0 and not (cust_getting_haircut)): # se nenhum cliente
estiver esperando ou recebendo corte
print (f ‘Cliente recebendo o corte: {cust_getting_haircut},
waiting_list: {waiting_list}’)
n_cust = int (input (“quantos clientes você quer?\n”)) # receber
entrada do número de clientes
chairs = int (input (“quantos acentos de espera você quer?\n”))
# receber entrada do número de clientes
if (n_cust > 11): # se o usuário quiser criar mais de 11 clientes
for i in range (12, n_cust +1): # cria mais exemplos de clientes
custs.append (f ‘Customer{i}’)
print (“gerando clientes...”)
for index, cust in enumerate (custs [:n_cust]):# para todos os
exemplos, inicializar e começar as threads
sp(1) # espera 1 segundo
customer_thread = threading.Thread (name = “Customer”,
target=customer, args=[f ‘ {cust} ({str(index +1)})’])
customer_thread.start()
cust_threads.append(customer_thread)
sp (random.randint (0,3)) # espera por um número aleatório
entre 0 e 3 segundos