Arquivo para agosto 2009
C++: Uma introdução a Threads com boost::thread
Para quem ainda não conhece, Boost é um conjunto de bibliotecas C++ multiplataforma que trabalha bem com a STL e é muito bem aceita na comunidade. Já existem dez bibliotecas Boost incluídas no C++ TR1 e que estarão no C++0x além de muitas outras que estarão no C++0x e que atualmente não estão no TR1.
Para instalar o boost siga esse Getting Started.
Fazer threads com boost::thread é muito simples. Basta criar uma função ou um functor e passar pro construtor da classe boost::thread:
class functor
{
public: void operator()() {};
};
functor::operator()()
{
/* content */
}
void function()
{
/* content */
}
functor obj;
boost::thread tObj(obj); //passa uma cópia de obj
boost::thread tObjRef(boost::ref(obj)); //passa uma referência para obj
boost::thread tFunc(&funcao);
// aguarda a finalização das threads
tObj.join();
tObjRef.join();
tFunc.join();
//ou pode-se criar grupos de threads
boost::thread_group my_threads;
my_threads.create_thread(obj);
my_threads.create_thread(boost::ref(obj));
my_threads.create_thread(&function);
my_threads.join_all(); // aguarda o grupo de threads retornar
Para ilustrar como funciona, fiz dois programinhas simples:
cin.cpp
#include <iostream>
#include <boost/thread/thread.hpp>
#include <boost/thread/xtime.hpp>
//determina se o usuário já entrou com o número
bool flag = false;
struct in
{
int n;
in() : n(0) {};
void operator()()
{
//try to read the number
while (!(std::cin >> n)) {
std::cin.clear();
std::cin.ignore(10000, '\n');
std::cout << "Enter a valid number" << std::endl;
}
flag = true;
}
};
void my_alarm()
{
while (!flag) {
//shows the message
std::cout << "Enter a number" << std::endl;
boost::xtime time;
boost::xtime_get(&time, boost::TIME_UTC); //current time
time.sec += 5; //adds 5 secs to time
//sleep 5 secs
boost::thread::sleep(time);
}
//exiting function
std::cout << "Alarm finished" << std::endl;
}
int main()
{
in test;
//create the two threads
boost::thread tin(boost::ref(test));
boost::thread tal(&my_alarm);
// wait
tin.join();
tal.join();
//show the user's number
std::cout << "Ok " << test.n << std::endl;
return 0;
}
Esse programa é composto por duas threads (fora o fluxo principal) uma que é responsável por fazer a leitura de um número e a outra que fica a cada 5 segundos mostrando uma mensagem “Enter a number” enquanto o usuário não digitar um número válido. Quando o usuário digita um número válido, a
flag é setada e os loops das duas threads são finalizados. Finalmente quando as duas threads são finalizadas, o fluxo normal de main() é reestabelecido e é mostrado o número digitado pelo usuário.
Lembrando que para usar boost::thread é necessário “linkar” a biblioteca ao programa. No caso do Linux é necessário linkar também a biblietca pthread em Windows “linkar” a win32api. No meu caso preferi usar linkagem estática.
murilo@blacksheep:~/programacao/boost$ g++ cin.cpp libboost_thread-gcc43-mt-1_39.a -o cin -lpthread
murilo@blacksheep:~/programacao/boost$ ./cin
Enter a number
Enter a number
naoehumnumero
Enter a valid number
Enter a number
4
Alarm finished
Ok 4
O outro exempo que fiz é um programinha que faz a multiplicação de duas matrizes e “simultaneamente” (depende de processador e SO) mostra uma barra de progresso com o progresso da operação.
multiplica.cpp
#include <iostream>
#include <boost/thread/thread.hpp>
#include <boost/thread/xtime.hpp>
const int MAX = 1000;
int A[MAX][MAX];
int B[MAX][MAX];
int C[MAX][MAX];
int porcentagem = 0;
void multiplica()
{
int soma;
for (int i = 0; i < MAX; i++) {
for (int j = 0; j < MAX; j++) {
soma = 0;
for (int k = 0; k < MAX; k++) {
soma += A[i][k] * B[k][j];
}
C[i][j] = soma;
}
if (i % 10 == 0) porcentagem++;
}
}
void barra_de_progresso()
{
int atual = 0;
while (porcentagem != 100) {
if (atual != porcentagem) {
if (porcentagem % 10 == 0) {
std::cout << "\b\b" << porcentagem << '%';
}
else std::cout << '=' << std::flush;
atual++;
}
}
}
int main()
{
boost::thread tMultiplica(&multiplica);
boost::thread tProgresso(&barra_de_progresso);
boost::xtime inicio, fim;
boost::xtime_get(&inicio, boost::TIME_UTC);
std::cout << "Iniciando a multiplicação de matrizes" << std::endl;
tMultiplica.join();
tProgresso.join();
boost::xtime_get(&fim, boost::TIME_UTC);
std::cout << "\n\nProcesso terminado em " <<
(fim.sec - inicio.sec) << " segundos" << std::endl;
return 0;
}
Saída:
murilo@blacksheep:~/programacao/boost/thr$ g++ multiplica.cpp libboost_thread-gcc43-mt-1_39.a -lpthread -o multiplica
murilo@blacksheep:~/programacao/boost/thr$ ./multiplica
Iniciando a multiplicação de matrizes
=======10%=======20%=======30%=======40%=======50%=======60%=======70%=======80%=======90%=======100%
Processo terminado em 43 segundos
Claro que o que menos nos importa nesse momento é o valor das matrizes e a multiplicação em si, por isso nem me dei ao trabalho de inicializar as matrizes nem de fazer nada com a matriz resultante.
Acredito que para uma introdução está bom e deu pra mostrar como é fazer programas com boost::thread com esses simples exemplos. Mais exemplos você pode encontrar na pasta libs do boost.
Links:
Como programar em C Orientado a Objetos
Olá, hoje eu vou dar um tempo na série “Coisas simples de se fazer em C++ que alguns ainda complicam” e irei falar sobre uma experiência minha tentando alguma maneira de programar em C orientado a objetos.
Sei que muitos vão pensar “pra quê isso?” ou dizer que é péssimo fazer isso ou coisa parecida, mas o intuito desse post é outro: é mostrar o que é possível fazer ou até mesmo enxergar alguma utilidade nisso. Nesse blog, procuro colocar coisas diferentes, curiosidades sobre programação e linguagens, porque acho que conteúdo “normal” já existem em muitos lugares na grande rede.
O que me instigou a fazer isso foi um post no CODARE “C: Escondendo o conteúdo de structs com tipos incompletos” de autoria do Thiago Santos, no qual ele cita sobre usar com orientação a objetos em C.
Li, achei interessante e quis, vamos dizer assim, dar um cara mais parecida de orientação a objetos ao que ele fez.
O primeiro passo foi pensar em como funcionam os famosos objetos.
Uma classe em uma linguagem orientada a objetos geralmente tem 2 tipos de elementos:
- Elementos de classe: elementos (funções e variáveis estáticas) que só são criados uma vez, todos os objetos da classe tem acesso ao mesmo elemento.
- Elementos de instância (ou objeto): elementos (atributos) que são criadas para cada objeto instanciado.
Ou seja:
class Animal
{
int age;
std::string specie;
static int count;
public:
int birthday();
};
No exemplo acima, age e specie são elementos de instância, ou seja, cada objeto têm o seus próprios. Já count e birthday() são de classe, pois todos os objetos dessa classe utilizarão o mesmo. O que ocorre no caso de funções é que na chamada da função, um ponteiro do objeto chamador é passado para a função. Isso faz com que não precise a cada instância de objeto criar uma nova função já que elas fazem a mesma coisa.
Algo como:
Animal animal, animal2; //lembrando que é APENAS uma ilustração do que acontece //não é assim que é realmente implementado mas é a mesma idéia //uma função só para todos os objetos de uma classe animal.birthday(); //vtable::animal::birthday(&animal); animal2.birthday(); //vtable::animal::birthday(&animal2);
A função birthday() é a mesma para as duas chamadas, o que muda é o ponteiro para os dados de cada objeto.
A minha implementação seguiu a idéia do Thiago Santos: uma classe person que tem os atributos name e age funções para instanciar, imprimir o nome e idade e deletar o objeto.
Vamos então dar uma olhada no nosso headerperson.h:
#ifndef PERSON_H__
#define PERSON_H__
//Incomplete type declaration
typedef struct person_private person_private;
typedef struct person {
//"private" data.
person_private* data;
//"class" functions
void (*free)();
void (*print)();
} person;
//instatiate a new person
person* new_person(const char*, int);
//pointer to the actual person in the context
person* __actual_person;
//sets the actual person
person* _(person* obj);
#endif
Nosso header contém a declaração incompleta de person_private leia aqui para saber o porquê, a definição da nossa “classe” person, as declarações das funções new_person() e _() e um ponteiro __actual_person.
Nossa “classe” person contém um ponteiro data para os dados que não poderão ser acessíveis através do objeto (name, age) além de ponteiros para funções (que serão nossos “métodos”).
A função new_person simplesmente instancia um objeto do tipo person.
O ponteiro __actual_person irá funcionar como o ponteiro passado para as funções de classe. Através do ponteiro saberemos qual objeto chamou a função.
A função _() seta o ponteiro __actual_person para que as funções sejam corretamente chamadas. Nota: não é thread safe. (hehehe)
Vamos ao nosso person.c:
#include "person.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
//private data... visible only by the functions below
struct person_private
{
char* name;
int age;
};
//a "manual destructor"
void free_person()
{
free(__actual_person->data->name);
free(__actual_person->data);
free(__actual_person);
puts("Person sucessfuly freed!\nBye");
}
//prints
void print_person()
{
printf("%s -: %d\n", __actual_person->data->name,
__actual_person->data->age);
}
person* new_person(const char* name, int age)
{
//Allocate the object
person* new = (person*)malloc(sizeof(person));
new->data = (person_private*)malloc(sizeof(person_private));
//Initialize the data
new->data->name = (char*)malloc(strlen(name) * sizeof(char) + 1);
strcpy(new->data->name, name);
new->data->age = age;
//Set the functions pointers
new->print = print_person;
new->free = free_person;
return new;
}
//must call the objects with this function
person* _(person* obj)
{
__actual_person = obj;
return obj;
}
O que temos aí é a função _() que seta o objeto atual (__actual_person) para que possa ser utilizado a função com o objeto certo, a função new_person que funciona como nosso construtor, as outras duas funções que são os “métodos da classe” print e free e a definição da estrutura person_private que contém os atributos da classe.
Os comentários ajudam no resto
.
Vamos então ver o main.c que é o nosso teste:
#include <stdio.h>
#include "person.h"
int main()
{
//Instantiates 2 persons
person* person1 = new_person("Murilo", 21);
person* person2 = new_person("Rovane", 47);
//print
_(person1)->print();
_(person2)->print();
//free
_(person1)->free();
_(person2)->free();
}
Eis o resultado:
murilo@blacksheep:~/programacao/cobject$ gcc main.c person.c
murilo@blacksheep:~/programacao/cobject$ ./a.out
Murilo -: 21
Rovane -: 47
Person sucessfuly freed!
Bye
Person sucessfuly freed!
Bye
Nota: deve-se usar sempre a função _() para utilizar o objeto.
Fiquei pensando se tem como fazer uma espécie de gerador de classes nesse formato no próprio C (com macros por exemplo). Vou procurar saber se existe algo a respeito, são 04h40min da mardugada não estou mais com boas idéias.
Qualquer sugestão, bug, idéia, reclamação, estamos aí!