# Threads

Agora que entendemos como funciona o forking de processos, vamos explorar as **threads**. Threads são uma forma mais leve de concorrência, permitindo que múltiplas tarefas sejam executadas dentro do mesmo processo, compartilhando o mesmo espaço de memória.

Conforme explicado na primeira parte do guia, no sistema operacional podemos criar threads através da chamada de sistema **clone** com os argumentos corretos. Na linguagem C, conseguimos manipular threads do sistema operacional, também chamadas de **kernel threads**.

Entretanto em C, embora podemos, não precisamos chamar a syscall **clone** diretamente devido à sua complexidade inerente. Mas para nossa sorte, na biblioteca padrão temos acesso a um cabeçalho chamado **pthread.h**, que abstrai de forma muito mais simples a criação de kernel threads utilizando [POSIX Threads](https://en.wikipedia.org/wiki/Pthreads) (ou *pthreads*), que são o padrão em sistemas UNIX-like:

<figure><img src="/files/Mtv69zCfLtmU3hp16kTV" alt="" width="563"><figcaption></figcaption></figure>

{% code title="pthreads.c" %}

```c
#include <stdio.h>
#include <pthread.h>

void* handle() {
	printf("Hello from thread!\n");
	return NULL;
}

int main() {
	pthread_t thread;

	// Cria uma thread
	pthread_create(&thread, NULL, handle, NULL);

	printf("Hello from main thread!\n");
	return 0;
}
```

{% endcode %}

A função *pthread\_create* recebe:

* um ponteiro para a variável que irá referenciar a thread na memória (**pthread\_t**)
* um ponteiro para a função que será executada no contexto da thread
* outros argumentos opcionais, que iremos deixar como NULL

Ao executar por algumas vezes o programa, podemos perceber a inconsistência nas mensagens, muitas vezes imprimindo apenas:

```
Hello from main thread
```

Bom, se você leu direitinho a primeira parte do guia, vai se lembrar que:

> Todo programa tem uma thread principal

Ou seja, quando o processo é iniciado, ele é encapsulado dentro de uma thread chamada "principal", pelo que quando falamos do processo em si, estamos também falando desta thread principal.

Entretanto, podemos ver que a mensagem da thread criada com `pthread_create` não apareceu na saída, e isto se deve à natureza de concorrência do escalonamento de tarefas do sistema operacional, onde verificamos em ação no tópico anterior com forking de processos.

Se eu rodar o programa, pode ser que a thread foi escalonada rapidamente e a mensagem aparece com sucesso. Mas pode ser também que a thread ainda não foi escalonada e o programa principal já foi finalizado. Enfim:&#x20;

> Não temos controle algum sobre a ordem e execução das tarefas no sistema operacional!

Como fazer com que o programa principal "espere" uma ou mais threads em execução serem finalizadas?

## Thread Join

Com a função `pthread_join` , o contexto da thread será trazido para o mesmo contexto da thread principal, então na prática o programa irá esperar pela execução da thread até que ela seja finalizada:

{% code title="pthreads.c" %}

```c
#include <stdio.h>
#include <pthread.h>

void* handle() {
	printf("Hello from thread!\n");
	return NULL;
}

int main() {
	pthread_t thread;

	// Cria uma thread
	pthread_create(&thread, NULL, handle, NULL);

	// Aguarda a thread terminar
	pthread_join(thread, NULL);

	printf("Hello from main thread!\n");
	return 0;
}
```

{% endcode %}

Saída:

```
Hello from thread!
Hello from main thread!
```

*Wow! How cool is that?*

## Um exemplo mais robusto

Já vimos os building blocks para criação de threads em C. Agora, vamos a um exemplo um pouco mais robusto, similar ao que aprendemos no tópico de forking de processos:

{% code title="pthreads-complex.c" lineNumbers="true" %}

```c
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void* handle(void* arg) {
	int thread_id = *((int*)arg); // Recebe o ID da thread
	printf("Thread %d is running...\n", thread_id);
	sleep(2); // Simula uma tarefa que leva 2 segundos
	printf("Thread %d is finished\n", thread_id);
	return NULL;
}

int main() {
	int num_threads = 3;
	pthread_t threads[num_threads];
	int thread_ids[num_threads];

	for (int i = 0; i < num_threads; i++) {
		thread_ids[i] = i + 1; // Identificação das threads
		pthread_create(&threads[i], NULL, handle, &thread_ids[i]);
	}

	for (int i = 0; i < num_threads; i++) {
		pthread_join(threads[i], NULL); // Aguarda cada thread terminar
		printf("Thread %d has been finished.\n", i + 1);
	}

	printf("All threads are finished.\n");
	return 0;
}
```

{% endcode %}

```
Thread 1 is running...
Thread 2 is running...
Thread 3 is running...
Thread 3 is finished
Thread 2 is finished
Thread 1 is finished
Thread 1 has been finished.
Thread 2 has been finished.
Thread 3 has been finished.
All threads are finished.
```

Repare que a ordem de execução pode mudar, devido à (e lá vamos novamente repetir) natureza da concorrência.

## Diferenças entre forking e threads

Até o momento, exploramos 2 formas de concorrência em C que são forking de processos e threads.&#x20;

No forking, a comunicação entre processos (IPC) precisa ser feita através de pipes ou forçar algum mecanismo de compartilhamento de memória, pois por padrão os processos não compartilham memória uns com os outros.

Ja com threads, cada thread compartilha o mesmo contexto que é a memória do **processo principal**, ou seja, duas ou mais threads podem provocar um cenário de condição de corrida se precisarem ler/escrever no mesmo recurso.

Para mitigar problemas de acesso a recurso compartilhado entre threads, precisamos recorrer ao uso de **locks**.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://concorrencia101.leandronsp.com/parte-ii-concorrencia-em-diferentes-linguagens/concorrencia-em-c/threads.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
