Threads

Threads são coisas fantásticas, você pode executar mais de uma coisa ao mesmo tempo com elas :O

Obviamente, caso você não tenha mais de um processador, não é exatamente ao mesmo tempo que as coisas são executadas, mas sim aos poucos. O assunto de threads é muito complexo, e não vou de forma alguma explicar aqui todos os seus problemas, suas diferenças com fork, ou como resolver coisas do tipo Race Condition, vou apenas mostrar uma introdução básica às mesmas, e mostrar como criar uma simples thread, e a diferença básica entre ela e o fork.

Diferença entre thread e fork

Basicamente, fork cria uma cópia do programa e executa o mesmo como se fosse uma thread, ou melhor, um programa novo rodando ao mesmo tempo. Isso significa que esse novo programa terá todas as suas próprias variáveis, existem formas de fazer um programa se comunicar com o outro, porém inicialmente, isso não é possível.
Já as threads executam uma parte do programa (uma função), que compartilham o mesmo endereço de memória, afinal, é o mesmo programa, logo as variáveis podem ser intercaladas entre os processos.

Como criar uma thread

Vamos utilizar as famosas Posix Threads (pthreads) que é o padrão do Linux.
Basicamente, existe uma função chamada pthread_create(), que recebe como parâmetros um ponteiro para um identificador de thread, um ponteiro para uma estrutura com as configurações da thread, um ponteiro para uma função do tipo void, e um ponteiro para as variáveis , também do tipo void.

Ex:

.
.
.
typedef struct thread_struct struct_t;
.
.
.
void *teste_thread();
.
.
.
int main(int argc, char *argv[])
{
.
.
.
pthread_t thread_id;
struct_t param_thread1;
.
.
.
pthread_create(&thread_id,NULL,&teste_thread,&param_thread1);
.
.
.
return 0;
}

Podemos ver que passamos NULL onde deveríamos ter passado uma estrutura com as características da thread, se passarmos NULL, as características default serão utilizadas.
Utilizar uma estrutura como parâmetro para a função que é uma thread é uma forma interessante de passar mais de um valor, de uma forma bem simples.
O problema com um código desses, é que se formos executar algo do tipo, e a thread demorar mais para terminar do que o resto do programa, a thread será completamente perdida. Para evitar isso, utilizamos uma função chamada pthread_join(), que além de garantir que a thread será finalizada antes do final do programa, ela também permite o recebimento de um retorno pela thread. Ela recebe como parâmetros a thread_id a fazer o join, e um ponteiro para um void, para guardar o retorno.

Ex:
pthread_join(thread_id,(void*)&done);

O programa completo

Esse programa não faz absolutamente nada de interessante, porém mostra como funcionam as threads.
Para compilar, você deve linkar o mesmo à libpthread, ou seja:

$gcc -o thread_test thread_test.c -lpthread

#include <pthread.h>
#include <math.h>
#include <time.h>
#include <stdio.h>

struct thread_struct
{
  int num;
  int vezes;
}thread_struct;
typedef struct thread_struct struct_t;
void *teste_thread(void *arg)
{
  struct_t *param = (struct_t*)arg;
  srand(time(NULL));
  param->num=rand();
  param->vezes++;
  return (void*)1;
}
int main(int argc, char *argv[])
{
  int vezes = 0;
  int i = 0;
  pthread_t thread_id;
  int done = 0;
  if(argc<2) vezes=10;
  else vezes=atoi(argv[1]);
  struct_t param_thread1;
  param_thread1.num=0;
  param_thread1.vezes=0;
  for(i=0;i<vezes;i++)
    pthread_create(&thread_id,NULL,&teste_thread,&param_thread1);
  for(i=0;i<vezes;i++)
    pthread_join(thread_id,(void*)&done);

  fprintf(stdout,”As threads estão sendo calculadas…\n”);
  fprintf(stdout,”num=%d\nvezes=%d\n”,param_thread1.num,param_thread1.vezes);
  return 0;
}

About Zarnick

Programer, sysadmin, guitarrist, and Italian. That's what I am. Plain simple.
ANSI C, C/C++