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