-
Notifications
You must be signed in to change notification settings - Fork 0
[GUIDE] Threads e Routines
Em gerenciamento de processos, um programa em execução é chamado de processo e uma thread é uma unidade básica de execução. Isso significa que cada programa pode ter um número de processos associados a ele, e cada processo pode ter um número de threads o executando.
Cada Thread tem:
- Um ID
- Um Program Counter
- Um Register Set
- Uma Stack Ela compartilha com outras threads pertencentes ao mesmo processo sua seção de código, seção de data/dados e outros recursos do sistema operacional, como arquivos e sinais.
Um processo tradicional tem uma única thread de controle. Se um processo tem múltiplas, pode performar mais de uma tarefa ao mesmo tempo.
Race conditions são um problema comum em programas multi-thread. Elas acontecem quando múltiplas tarefas ou threads acessam um mesmo recurso compartilhado sem proteções o bastante, levando a comportamento indefinido e imprevisível.
Em outras palavras, pode acontecer quando 2 ou mais threads tentam acessar, ler ou modificar, a mesma variável ao mesmo tempo. Pode levar a um erro no valor final da variável e a comportamento indefinido.
A solução para race conditions. É como um cadeado (lock) que protege um bloco de código e só pode ser executado pelo dono do cadeado até que ele o destranque.
Dessa forma, uma thread pode trancar o cadeado, modificar a variável, e só então destrancar. Se uma segunda thread chegar ao mesmo tempo, não vai conseguir modificar a variável por causa do mutex aplicado pela outra.
É importante inicializar e destruir o mutex após terminar de usá-lo, ou não vai funcionar.
São blocos de código, como uma função, que uma Thread executa de maneira independente do resto do programa. Também são chamadas de tarefas, Tasks.
Nosso programa terá duas:
É o loop de trabalho de cada coder. As etapas dela são respectivamente:
- Checar através da dead_flag se a simulação terminou.
- Se não terminou, rode o ciclo completo:
- Pegue os dois dongles (requisição feita sequencialmente, leia [GUIDE] Dongles (priority queue e scheduler
- Faça o log dos dois dongles.
- last_compile = get_current_time()
- compiling_score++
- Faça o log (Coder X is compiling)
- Compile
- Devolva os dongles
- Faça o log (Coder X is debugging)
- Faça o log (Coder X is refactoring)
Não esqueça que a cada ação do coder (compiling, debugging e refactoring) existe um sleep() referente ao tempo passado nos argumentos do programa. E também não esqueça dos mutexes/locks, eles foram criados no t_simulation e passados por referência a cada t_coder por um motivo, use-os.
Será responsável por monitorar a simulação, checando a cada 1ms, e a parar quando chegar ao fim, dentro dos 10ms de tolerância.
As etapas dela são, respectivamente:
- While(1):
- Checar se algum coder teve burnout ou se todos completaram o quota. Se sim, break.
- Se não, usleep(1000), checar de novo a cada 1 ms.
- mutex_lock no dead_lock
- dead_flag = 1
- Unlock no dead_lock
- pthread_cond_broadcast em TODOS os dongles para acordar todos os coders em suas filas de espera. (sem isso, eles só retornariam quando o cooldown expirasse).
- Se houve burnout, faça o log do burnout. Simulação finalizada.