Escolar Documentos
Profissional Documentos
Cultura Documentos
Trabalho Prático 5:
Solução concorrente para identificação de números primos
1 Introdução
Este trabalho prático possui como objetivo realizar o desenvolvimento de uma aplicação concorrente
para o problema da identificação dos n-primeiros números primos, na qual será executado por tem-
pos distintos com diferentes quantidades de threads e comparada uma solução sequencial com uma
solução paralela (concorrente).
2 Metodologia
Utilizando o compilador g++ da linguagem c++ num ambiente Linux e fazendo uso da biblioteca de thre-
ads intitulada pthread, foi elaborado um programa que recebe um número arbitrários de threads como
argumento. Em casa de ausência de argumento, a solução sequencial do programa será executada.
O programa abrirá um quantidade de threads, cada uma acessando de forma concorrente uma variável
que indica um número a ser verificado como primo. A thread pega este número, incrementa a variável
para as outras threads realizarem a verificação do próximo e calcula se ele é primo. Em caso positivo,
realiza a impressão na tela (ou salvamento em arquivo) juntamente com o id da thread. Para gerenciar
o acesso concorrente (a variável que guarda o número e o print do resultado na tela) foram utilizados
mutexes da pthread.
Na abordagem sequencial, apenas foi utilizado um FOR que efetua a divisão com todos os números
menores ao número que está verificando se é primo. Caso seja possível apenas duas divisões sem resto
(divisão por 1 e o próprio número), concluímos que de fato é um número primo. Portanto, efetua a
Sistemas Distribuídos
Lucas Pantuza Amorim
impressão do número e o incrementa (para realizar outra verificação). O programa está programado
para fazer isso infinitamente, até que seja encerrado manualmente,
Finalizado a parte da implementação, partimos para a coleta dos dados. Para isso, fazendo uso do
terminal do Linux, utilizamos o comando ”g++ -o tp5 main.cpp -l pthread” para compilar o programa
e então ”./tp5 24 > Resultados/30min_24threads.txt” para iniciar. Esse comando executa o programa
por meio do arquivo chamado ’tp5’ fazendo uso de 24 threads, durante 30 minutos e salva em arquivo
’.txt’ para que possamos comparar a quantidade de números primos encontrados em cada situação
posteriormente. Repare que, o processo de execução é manual, desta forma, com o auxílio de um
cronômetro, o programa foi também manualmente encerrado (Ctrl + C).
O programa foi executado por 30 minutos com 24, 12, 6, 3 e 1 thread. Posteriormente, o mesmo proce-
dimento foi realizado durante 15 minutos, 5 minutos, 1 minuto, 30 segundos, 10 segundos e 5 segundos.
Uma exemplificação dos prints realizados dentro do arquivo .txt pode ser observado na Figura 3.
Ao término da geração dos números primos nas condições apresentadas acima, precisamos contar
quantos números primos foram encontrados em cada execução. Para isso, através do comando ”wc
-l Resultados/30min_24threads.txt” contamos a quantidade de linhas e consequentemente de núme-
ros primos encontrados (cada linha possui a impressão de um número primo). O resultado pode ser
observado na Figura 4 presente na seção de resultados.
Por fim, com o objetivo de comparar as diferentes abordagens, foi então gerado alguns gráficos que
podem ser conferidos a partir da figura 7 presente na seção Resultados abaixo.
3 Resultados
(a) (b)
3.7 Gráficos
4 Análise
Esperava-se que a solução concorrente fosse capaz de encontrar uma quantidade superior à solução
sequencial e observamos que tal comportamento se confirmou. Porém, esperava-se que quanto maior
a quantidade de threads, maior a quantidade de números primos encontrados no mesmo intervalo de
tempo, porém isto não ocorreu. Percebe-se portanto que a quantidade de números a serem encontra-
dos não está relacionado com a quantidade de threads, mas sim, como estas foram programadas em
harmonia com o tempo dedicado a execução das mesmas.
5 Conclusão
Após realização do trabalho prático concluímos que, de fato a solução concorrente foi capaz de desco-
brir uma quantidade superior de números primos se comparado a versão sequencial. Adicionalmente,
observamos ainda, de maneira prática, através da utilização um gerenciador de tarefas, que a solução
concorrente faz melhor uso dos núcleos do processador, diferentemente da solução sequencial, justifi-
cando portanto tal comportamento.
6 Código
Todo programa foi implementado em um único arquivo (main.cpp), que pode ser obervada abaixo:
1 / / b i b l i o t e c a n a t i v a de m a t e m a t i c a do C
2 # i n c l u d e < math . h >
3
4 / / l i b de t h r e a d s do C
5 #include < pthread . h>
6
7 / / b i b l i o t e c a n a t i v a de f u n c i o n a l i d a d e s b a s i c a s de i o C + +
8 #include < iostream >
9
10 / / p a r a n a o p r e c i s a r p o r s t d a n t e s d a s f u n c o e s da l i b anterior
11 u s i n g namespace s t d ;
12
13 / / a l i a s p a r a um i n t e i r o p o s i t i v o m u i t o g r a n d e
14 using b i g I n t = unsigned long long ;
15
16 / / n u m e r o a t u a l p a r a c h e c a r s e eh p r i m o
Sistemas Distribuídos
Lucas Pantuza Amorim
17 b i g I n t num = 1 ;
18
19 / / r e f e r e n c i a ao m u t e x de a c e s s o ao n u m e r o
20 p t h r e a d _ m u t e x _ t num_mutex ;
21 / / m u t e x p a r a p r i n t a r na t e l a
22 / / c o u t e m b o l a d a d o s de m u l t i p l a s t h r e a d s
23 pthread_mutex_t print_mutex ;
24
25 / / r e t o r n a t r u e ou f a l s e i n d i c a n d o s e o n u m e r o eh p r i m o
26 / / r e c e b e um n u m e r o i n t e i r o n a o n e g a t i v o
27 b o o l e h P r i m o ( b i g I n t num ) {
28
29 / / 0 e 1 n o s o primos
30 i f ( num < = 1 ) r e t u r n f a l s e ;
31
32 / / o maximo d i v i s o r p o s s i v e l de um n u m e r o eh a r a i z q u a d r a d a d e l e msm
33 / / nesse ’ f o r ’ tentamos todas a d i v i s o e s p o s s i v e i s
34 f o r ( i n t d i v i s o r = 2 ; d i v i s o r < = s q r t ( num ) ; d i v i s o r + + ) {
35 / / s e d i v i s a o n a o t e r r e s t o o n u m e r o n a o eh p r i m o
36 i f ( num % d i v i s o r = = 0 ) r e t u r n f a l s e ;
37 }
38
39 / / nenhuma d i v i s a o foi sucedida , n u m e r o eh p r i m o
40 return true ;
41 }
42
43 / / c h e c a s e o n u m e r o a t u a l eh p r i m o
44 / / r e c e b e o i d da t h r e a d que chamou a f u n c a o
45 void checaAtual ( int id ) {
46
47 / / a c e s s o e x c l u s i v o a num n e s s e t r e c h o
48 p t h r e a d _ m u t e x _ l o c k ( & num_mutex ) ;
49 / / pega o numero a t u a l p a r a c h e c a r
50 b i g I n t c h e c a r = num ;
51 / / muda p a r a o s o u t r o s c h e c a r e m o p r o x i m o n u m e r o
52 num + + ;
53 / / l i b e r a a c e s s o ao num
54 p t h r e a d _ m u t e x _ u n l o c k ( & num_mutex ) ;
55
56
57 / / i m p r i m e o numero c h e c a d o s e f o r p r i m o
58 i f ( ehPrimo ( checar ) ) {
59 / / a c e s s o e x c l u s i v o a s a i d a do r e s u l t a d o n e s s e t r e c h o
60 pthread_mutex_lock (& print_mutex ) ;
61 cout << id << " : " << checar << endl ;
62 / / l i b e r a a c e s s o ao p r i n t do r e s u l t a d o
63 pthread_mutex_unlock (& print_mutex ) ;
64 }
Sistemas Distribuídos
Lucas Pantuza Amorim
65
66 }
67
68 / / f u n c a o que vamos e x e c u t a em c a d a t h r e a d
69 void * thread ( void * thread_id ) {
70 // converte o ponteiro recebido
71 / / p a r a um p o n t e i r o de i n t e i r o
72 // e e x t r a i o valor naquele endereco
73 int id = *( ( int *) thread_id ) ;
74 / / f i c a para sempre checando primos
75 while ( true ) { checaAtual ( id ) ; }
76 r e t u r n NULL ;
77 }
78
79
80 void sequencial () {
81
82 int i = 2 , totalDivisores , j ;
83
84 while ( true )
85 {
86 t o t a l D i v i s o r e s = 0;
87
88 / / Conta os d i v i s o r e s para v e r i f i c a r se o v a l o r primo
89 for ( j = 1 ; j <= i ; j + + )
90 {
91 if ( i % j = = 0)
92 { // Se r e s t o z e r o
93 totalDivisores ++;
94 }
95 }
96 / / Se o t o t a l de d i v i s o r e s dois , primo
97 i f ( t o t a l D i v i s o r e s == 2)
98 {
99 p r i n t f ( " %4d \ n " , i ) ; / / I m p r i m e o p r i m o com f o r m a t a o de 4
casas
100 }
101
108
109 / / f u c n a o p r i n c i p a l , r e c e b e o n u m e r o de a r g u m e n t o s de l i n h a de comando
110 / / e q uai s sao e s s e s argumentos
111 i n t main ( i n t a r g c , c h a r * a r g v [ ] ) {
Sistemas Distribuídos
Lucas Pantuza Amorim
112
113 / / / / s e o n u m e r o de t h r e a d s n a o f o r p a s s a d o como a r g u m e n t o e n c e r r e o
programa
114 // i f ( argc < 2 ) {
115 // c o u t < < " p a s s e o n u m e r o de t h r e a d s " < < e n d l ;
116 // return 1;
117 // }
118
119 / / s e o n u m e r o de t h r e a d s n a o f o r p a s s a d o como a r g u m e n t o , e x e c u t a a
v e r s o sequencial
120 i f ( argc < 2 ) {
121 sequencial () ;
122 }
123
124
125 / / s a l v a o n u m e r o de t h r e a d s
126 / / o a r g u m e n t a c h e g a como s t r i n g , c o n v e r t e m o s e l e p a r a um i n t e i r o
127 / / um a r g u m e n t o i n v a l i d o r e t o r n a 0
128 i n t n _ t h r e a d s = s t r t o l ( a r g v [ 1 ] , NULL , 1 0 ) ;
129
130 / / se f o r 0 t h r e a d s ( argumento i n v a l i d o )
131 // consideramos 1 thread
132 i f ( n _ t h r e a d s <= 1 ) n _ t h r e a d s = 1 ;
133
134 / / i n i c i a m u t e x do p o s s i v e l num p r i m o , s e f a l a r e n c e r r a o p r o g r a m a
135 i f ( p t h r e a d _ m u t e x _ i n i t ( & num_mutex , NULL ) ! = 0 ) {
136 c o u t < < " f a l h a na i n i c i a l i z a o do m u t e x de num " < < e n d l ;
137 return 3;
138 }
139
140 / / i n i c i a m u t e x de p r i n t , se f a l a r e n c e r r a o programa
141 i f ( p t h r e a d _ m u t e x _ i n i t ( & num_mutex , NULL ) ! = 0 ) {
142 c o u t < < " f a l h a na i n i c i a l i z a o do m u t e x de p r i n t " < < e n d l ;
143 return 4;
144 }
145
146 / / v e t o r de r e f e r e n c i a s de t h r e a d s
147 pthread_t * threads = ( pthread_t *) malloc ( sizeof ( pthread_t *) *
n_threads ) ;
148 / / abre as threads
149 f o r ( i n t i = 0 ; i < n_threads ; i ++ ) {
150 / / i d da t h r e a d
151 / / p e g a um e n d e r e c o de m e m o r i a p a r a g u a r d a r i n t e i r o
152 i n t * id = ( i n t *) malloc ( sizeof ( i n t *) ) ;
153 / / g u a r d a v a l o r de i n a q u e l e e n d e r e c o
154 * id = i ;
155 // c r i a a thread
156 / / s e f a l h a r s a i do p r o g r a m a
Sistemas Distribuídos
Lucas Pantuza Amorim
codigo/main.cpp
Sistemas Distribuídos
Lucas Pantuza Amorim
Referências
[1] Greg Ippolito. POSIX thread (pthread) libraries, 2004. URL
https://www.cs.cmu.edu/afs/cs/academic/class/15492-f07/www/pthreads.html.
[4] Kishlay Verma. Mutex lock for Linux Thread Synchronization - GeeksforGeeks, 11 2019. URL
https://www.geeksforgeeks.org/mutex-lock-for-linux-thread-synchronization/.