. Introducao-a-Concorrencia
Esse documento representa parte dos meus estudos acerca do Livro - Operational Systems Three Easy Pieces, de dominio publico.
Neste capitulo, introduzimos uma nova abstração para cada processo em execução: as threads. Um programa multi-threaded tem mais que um ponto de execução, ou seja, tem mais de um Program Counter, cada um deles sendo executado paralelamente.
O estado de uma thread eh bem similar a um processo. Cada thread tem seu conjunto de regs, ou seja, se 2 threads estão rodando no mesmo núcleo da CPU, urge a necessidade de usarmos novamente o context switch. .
Nos processos, usamos a BCP (Bloco de Controle de Processos), agora usaremos um BCT (Bloco de Controle de Threads). A diferença esta no fato de que o Address Space continua o mesmo, ou seja, há compartilhamento de memoria (não precisamos trocar page-tables entre threads). Entretanto, este address space vai estar diferente ao processo, ja que cada thread precisará de uma stack diferente. Toda memória alocada em cada na stack de cada thread leva o nome de armazenamento thread-local.
Criando uma thread (em C) e testando shared memory
Problemas da memoria compartilhada
Usar threads acaba gerando um problema um tanto quanto evidante: compartilhar memoria. Usar a mesma variavel e altera-la em 2+ threads pode criar uma race condition (de acordo com a ordem de context switches e memmory access, o resultado de variaveis pode ser diferente - uma thread pode acessar um valor nao alterado por outra, ou vice-versa, ordem que deveria).
Se uma thread executar uma parte do codigo gera (ou geraria) uma race condition, chamamos essa regiao de codigo como regiao critica.
Quando desejamos que uma regiao critica de memoria nao seja executada em diferentes threads, podemos garantir esse comportamento atraves da exclusão mútua. Essa propriedade garante que apenas uma thread rode aquela regiao de codigo, enquantos as outras nao poderão.
Atomicidade
[!tip] Operações Atômicas
Operações atômicas são sequências de instruções executadas sem interrupção, garantindo que sejam concluídas como uma unidade, ou seja, ou elas são completamente executadas, ou nem são chamadas.
Nesse sentido, podemos criar Operacoes Atomicas para evitar a corrupcao de dados que supracitamos e garantir a exclusao mutua. Por exemplo:
poderiamos criar a seguinte funcao em linguagem de maquina
memory-add 0x8049a1c, $0x1esse codigo atomico seria completamente executado de uma vez, conquanto traduzido para:
mov 0x8049a1c, %eax
add $0x1, %eax
mov %eax, 0x8049a1cComo este tipo de instrução nem sempre é comum e não poderemos contar com isso em todo ISA, devemos criar um conjunto de instruções que devemos tratar como átomos chamadas de synchronization primitives.
[!note] Programa indeterminado x deterministico
Um programa indeterminado consiste de um ou mais race conditions acontecendo varias vezes no programa, o que causa o resultado a ser nao deterministico, como se espera de uma funcao.

