C: Usando Threads

Hoje comecei a ler o man pthreads e resolvi tentar criar um programa que fizesse uso delas. Dependendo do que será feito não há muitas complicações, apesar do que pode parecer. Os problemas surgem quando, por exemplo, uma thread depende do resultado de uma outra, que ainda não terminou.

De qualquer forma, só vou demonstrar o básico aqui, como criar uma thread e pegar o valor que ela retorna.

Aqui só usaremos um tipo de variável e duas funções, mas é claro que as pthreads podem fazer muito mais que isso. O tipo que usaremos é o pthread_t que guarda informações sobre uma thread e as funções são pthread_create e pthread_join. A primeira cria uma nova thread, guardando informações sobre ela no seu primeiro argumento, lendo atributos do segundo (não os usaremos), chamando a função do terceiro argumento e passando o quarto argumento como argumento para função. A segunda associa o valor retornado por um thread determinada pelo primeiro argumento no segundo argumento.

A função que usei no caso foi o fatorial, simples e rápida, e por isso os detalhes relativos à implementação de threads ficam mais aparentes. Ei-la:

/* gcc -pthread */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h> /* sleep() */

struct nums
{
    int num[3];
};

void *fac(void *arg)
{
    struct nums *args = arg;
    struct nums *result = malloc(sizeof(struct nums));

    int i;

    result->num[0] = 1;
    result->num[1] = 1;
    result->num[2] = 1;

    for(i = args->num[0]; i > 0; i--)
    {
        result->num[0] *= i;
    }

    printf("%d\n", result->num[0]);
    sleep(1);

    for(i = args->num[1]; i > 0; i--)
    {
        result->num[1] *= i;
    }

    printf("%d\n", result->num[1]);
    sleep(1);

    for(i = args->num[2]; i > 0; i--)
    {
        result->num[2] *= i;
    }

    printf("%d\n", result->num[2]);
    sleep(1);

    return((void *) result);
}

int main(int argc, char *argv[])
{
    struct nums arg1;
    struct nums arg2;

    struct nums *res1;
    struct nums *res2;

    int ret = 0;

    pthread_t thread1, thread2;

    arg1.num[0] = 5;
    arg1.num[1] = 15;
    arg1.num[2] = 12;

    arg2.num[0] = 7;
    arg2.num[1] = 8;
    arg2.num[2] = 10;

    pthread_create(&thread1, NULL, &fac, &arg1);
    pthread_create(&thread2, NULL, &fac, &arg2);

    ret = pthread_join(thread1, (void *) &res1);
    if(ret == 0)
    {
        printf("returned: %d, %d, %d\n", res1->num[0],
                                         res1->num[1],
                                         res1->num[2]);
    }

    ret = pthread_join(thread2, (void *) &res2);
    if(ret == 0)
    {
        printf("returned: %d, %d, %d\n", res2->num[0],
                                         res2->num[1],
                                         res2->num[2]);
    }

    return(0);
}

Fac

Temos uma função fac que aceita um argumento do tipo void* e que retorna um valor do tipo void*. Infelizmente, até onde sei, não há como passar mais argumentos, então, se for necessário, o melhor é usar uma struct, como eu fiz.

A “pegadinha” aqui são esses void*s. Uma função do tipo void não retorna nada, logo, devemos supor, void* é um ponteiro que aponta para nada, ou seja, se ele aponta para nada, ele não aponta, então não é um ponteiro. Ele é o ponteiro que é e não é ao mesmo tempo.

Mentira, um void* é um ponteiro para qualquer coisa, int, char, um struct qualquer, por isso que o usamos neste caso.

Então temos o código em si do fatorial e depois um sleep(). O motivo pelo qual o coloquei é que eu não tenho um processador dual-core, então eu não teria como saber se as threads estão sendo executadas simultaneamente. Com o sleep(), como elas não fazem nada enquanto dormem, uma thread pode calcular o fatorial enquanto a outra dorme.

E por último, temos que retornar o valor, mas como o tipo da função é void* e não struct nums*, temos que fazer um cast, uma mudança de tipo. Por isso aquele (void *) dentro de return()

Main

Na função main(), a primeira coisa que merece destaque é o pthread_t. Como eu falei antes, são eles que vão guardar informações sobre as threads.

Depois, temos a chamada de pthread_create. Os argumentos são &thread1, que é um pthread_t, NULL, já que não vamos passar “opções” na hora de criar a thread, &fac que é a nossa função e &arg1 que é nossa struct com os argumentos. Fazemos basicamente a mesma coisa de novo, mas agora com a segunda thread.

Depois, nós pegamos os valores retornados pelas threads com pthread_join. O valor que ela retorna, no caso “ret” é para sabermos se houve erro ou não. O primeiro argumento é a thread que queremos (mas sem o & agora) e onde o valor de retorno será guardado, no caso, res1.

No fim, para compilar:

gcc arquivo.c -pthread
Publicidade

3 Respostas para “C: Usando Threads

  1. NiX março 4, 2009 às 2:17 pm

    Usar pthreads em alguns problemas pode ser a maneira mais fácil de paralelizar um algoritmo. Isto é, se o algoritmo permitir paralelização :)

    Eu mesmo já usei a biblioteca para rodar um algoritmo genético em paralelo (em dois processadores) e o tempo de execução caiu pela metade (o melhor ganho possível). Foram apenas 5 linhas para modificar o código, coisa rápida.

  2. Cooler_ fevereiro 12, 2010 às 1:56 pm

    Bom pthreads he baixo nivel prefiro usar fork, mas como não é muito portavel posix threads quebra o galho,Dev C++ mesmo tem ateh package

    bom quanto ao gcc eu uso “gcc -Wall -g -o code code.c” assim vejo os erros e posso depurar com GDB, muito bom seu post parabens ;)

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s

%d blogueiros gostam disto: