sábado, 25 de fevereiro de 2012

Sistemas Operacionais - Threads


Idéia básica

Processos: programa em execução que contém um único fluxo de execução.

Threads: programa em execução com múltiplos fluxos de execução.



Em sistemas tradicionais, cada processo tem seu espaço de endereçamento individual e apenas um fluxo de execução (thread). No entanto, algumas vezes desejamos ter vários fluxos de execução no mesmo espaço de endereçamento e uma execução paralela.

Usualmente as threads são divididas em duas categorias: thread ao nível do utilizador (em inglês: User-Level Thread (ULT)), e thread ao nível do núcleo (em inglês: Kernel-Level Thread (KLT)).

http://pt.wikipedia.org/wiki/Thread_%28ci%C3%AAncia_da_computa%C3%A7%C3%A3o%29

Visão Geral



Thread ou linha de execução é uma das maneiras utilizadas por um processo para dividir a si mesmo em duas ou mais tarefas que podem ser executadas simultaneamente, em geral, em arquiteturas multiprocessadas. O suporte à thread é fornecido pelo próprio sistema operacional (SO), no caso da Kernel-Level Thread (KLT), ou implementada através de uma biblioteca de uma determinada linguagem, no caso de uma User-Level Thread (ULT) [1].

Um exemplo pode ser dado através de um jogo onde o mesmo pode ser modelado com linhas de execução diferentes, sendo uma para desenho de imagem e outra para áudio. Nesta aplicação, teríamos uma thread para tratar rotinas de desenho e outra thread para tratar instruções do áudio. Para o usuário, a imagem é desenhada ao mesmo tempo em que o áudio é emitido pelos auto-falantes. No entanto, para sistemas com uma única CPU, cada linha de execução é processada por vez [1].

Assim, as threads permitem um paralelismo de granularidade mais fina (paralelismo de instruções). Por granularidade mais fina, subentende-se que a unidade de cálculo realizada de maneira concorrente é  menor  do  que  a  unidade  de  cálculo  associada  a  um  processo  (paralelismo  de  aplicações,  por exemplo). A granularidade de uma thread pode ser, por exemplo, um método ou um conjunto de instruções  dentro  de  um  programa.  Para  isso  ser  possível,  cada  thread  mantém  um  contador  de programa (PC Program Counter) e um registrador de instruções (IR- Instruction Register) para dizer qual instrução é a próxima a ser executada e qual está sendo processada, respectivamente. Além de registradores que contém suas variáveis atuais de trabalho, pilha com o histórico das instruções executadas e o estado de cada variável. Para que uma thread possa ser executada ela deve pertencer a algum processo, ou seja, um processo deve ser criado anteriormente. Dessa forma, podemos dizer que processos são usados para agrupar recursos (espaço de endereçamento com o código do programa, variáveis globais, etc) enquanto as threads são as entidades escalonadas pela CPU para a execução de tarefas.

Threads são conhecidas como  processos leves. Basicamente, esse atributo se deve ao menor tempo gasto em atividades de criação e escalonamento de threads, se comparadas aos processos. O compartilhamento de memória entre as threads maximiza o uso dos espaços de endereçamento e torna mais eficiente o uso destes dispositivos.

Como   todas   as   threads   tem  exatamente   o   mesmo   espaço   de   endereçamento,   elas   também compartilham as mesmas variáveis globais. Assim, como cada thread pode acessar qualquer posição de memória dentro do espaço de endereçamento do processo, é possível a uma thread ler, escrever ou até apagar informações usadas por outra thread. Não existe um meio de proteção para isso, fica sob responsabilidade do usuário este cuidado já que normalmente criamos threads para cooperar e não competir. O escalonamento entre threads acontece da mesma forma que entre processos.

Portanto, cada thread tem o mesmo contexto de software e compartilha o mesmo espaço de memória de um mesmo processo pai. Contudo, o contexto de hardware de cada fluxo de execução é diferente. Conseqüentemente, o tempo perdido” com o escalonamento das threads é muito menor do que o escalonamento de processos. Além disso, não há acesso protegido a memória nativamente (a implementação  é de responsabilidade  do programador) devido ao compartilhamento  do espaço de memória [1].

Uma das vantagens do uso de threads está no fato do processo poder ser dividido em mais de uma linha de execução. Quando uma thread está esperando determinado dispositivo de I/O ou qualquer outro recurso do sistema, o processo como um todo não precisa ser bloqueado, pois quando uma thread entra no estado de bloqueio uma outra thread do mesmo processo está aguardando na fila de prontos para dar continuidade a execução do programa [1].

Normalmente, criamos um processo com um único thread e a partir deste cria-se novas threads através de comandos do tipo thread_create( ). Geralmente passamos como parâmetro um nome de um procedimento para informar o que a thread deve executar. Como resultado do comando temos um identificador/nome para a thread criada. Ao finalizar seu trabalho a thread pode chamar uma função do tipo thread_exit e desaparecer para não ser mais escalonável.

Assim como os processos, as threads possuem estados durante o ciclo de vida. Os estados atingidos são  os  mesmos  discutidos  em  processos.  Uma  tabela  de  threads  deve  então  ser  mantida  para armazenar informações  individuais  de cada fluxo  de execução.  Essa tabela é denominada  TCB e contém:

1.   Endereço da pilha;
2.   Contador de programa;
3.   Registrador de instruções
4.   Registradores de dados, endereços, flags;
5.   Endereços das threads filhas;
6.   Estado de execução.

Resta para o processo, como um todo, informações do tipo endereço da área de trabalho, variáveis globais, apontadores para informações de arquivos abertos, endereços de processo filhos, informações sobre timers, sinais, semáforos e de contabilização.

Threads podem comunicar-se através das variáveis globais do processo que as criou. A utilização destas variáveis pode ser controlada através de primitivas de sincronização (monitores, semáforos, ou construções similares). Primitivas existem para bloqueio do processo que tenta obter acesso a uma área da memória que está correntemente sendo utilizada por outro processo. Primitivas de sinalização de fim de utilização de recurso compartilhado também existem. Estas primitivas podem acordar um ou
mais processos que estavam bloqueados.


Figura 1. Processos com um e com múltiplos threads.

 

Alguns Benefícios

1.   Velocidade  de Criação das Threads: as threads são mais fáceis de criar e destruir que os processos pois elas não tem quaisquer recursos associados a elas. Em alguns sistemas criar um thread pode ser cem vezes mais rápido do que criar um processo. No Solaris, criar um processo é aproximadamente 30 vezes mais lento do que criar um thread e a troca de contexto é aproximadamente 5 vezes mais lento.
2.   Capacidade  de  Resposta:  a  utilização  do  multithreading  pode  permitir  que  um  programa continue executando e respondendo ao usuário mesmo se parte dele está bloqueada ou executando uma tarefa demorada. Por exemplo, enquanto um navegador Web carrega uma figura ele permite a interação com o usuário.
3.   Compartilhamento  de Recursos:  todos  os recursos alocados  e utilizados  pelo processo  aos quais pertencem são compartilhados pelos threads.
4.   Economia: como os threads compartilham recursos dos processos aos quais pertencem, é mais econômico criar e realizar a troca de contexto de threads.
5.   Utilização de Arquiteturas Multiprocessadas: é possível executar cada uma das threads criadas para um mesmo processo em paralelo (usando processadores diferentes). Isso aumenta bastante os benefícios do esquema multithreading.
6.   Desempenho: obtido quando há grande quantidade de computação e E/S, os threads permitem que essas atividades se sobreponham e, logo, melhore o desempenho da aplicação.


Em arquiteturas  monoprocessadas  o paralelismo  obtido  na execução de threads CPU-Bound  Ã© aparente, pois a CPU fica alternando entre cada thread de forma tão rápida que cria a ilusão de paralelimo real. Portanto, neste caso, a execução das threads acontecem de forma concorrente assim como os processos. Entretanto, o paralelismo ocorre entre threads do tipo CPU-Bound e I/O- Bound.

Principais Métodos

  • run(): método que executa as atividades de uma thread, quando este método finaliza, a thread também termina. 
  • start(): método que dispara a execução de uma thread, ele chama o método run( ) antes de terminar.
  • sleep(int x): método que coloca a thread para dormir por x milisegundos.
  • join( ): método que espera o término da thread para qual foi enviada a mensagem para ser liberada
  • interrupt( ): método que interrompe a execução de uma thread.
  • interrupted(): método que testa se uma thread está ou não interrompida.

http://knol.google.com/k/threads#



 Exemplo de Threads em JAVA:


Exemplo1:

Neste exemplo duas threads são criadas com igual prioridade. Cada uma imprime os 100 primeiros inteiros seguidos da frase TERMINOU SANTOS! ou TERMINOU SÃO PAULO! Uma thread se chama Santos, e a outra, São Paulo.
Após cada linha impressa, a thread "dorme" um número aleatório de milissegundos - entre 0 e 399 - para evitar que o laço de impressão termine antes de se esgotar a fatia de tempo da thread (delta t). O nome é dado pelo argumento passado ao construtor da thread. O método getName() da classe Thread retorna a string com o nome:


public class ThreadSimples extends Thread {
public ThreadSimples(String str) {
super(str);
}

public void run() {
for (int i = 0; i < 100; i++) { System.out.println(i + " " + getName()); try {
sleep((long)(Math.random() * 400));
}
catch (InterruptedException e) {}
}
System.out.println("TERMINOU " + getName()+"!");
}
}

public class TesteDuasThreads {
public static void main (String[] args) { ThreadSimples santo = new ThreadSimples("SANTOS"); santo.setPriority(4);
ThreadSimples sampa =new ThreadSimples("SÃO PAULO"); sampa.setPriority(6); Thread.currentThread().setPriority(1); santo.start();
sampa.start();
System.out.println("Main terminado!");
}
}




Exemplo 2:

import java.lang.Thread;

public class SequencialA extends Object {

public static void main(String args[]) throws Exception {
int i;
exemplo ex2 = new exemplo();

exemplo TTA = new exemplo(); Thread thA = new Thread(TTA); thA.start();

for (i=0; i<10; i++) { ex2.f("normal"); Thread.sleep(1000);
}
}
}

class exemplo implements Runnable {
private int y;
public void run() {
f("run");
}
public void f(String x) {
y++;
System.out.println(x+" Valor de y: "+y);
}
}


Pergunta: O que o programa do exemplo 2 faz?





Exemplo do uso de threads em UNIX:

#include <stdio.h>
#include <pthread.h>
int global;
void *thr_func(void *arg);
int main(void)
{

pthread_t tid;
global = 20;
printf(“Thread principal: %d\n”, global); pthread_create(&tid, NULL, thr_func, NULL); pthread_join(tid, NULL);
(“Thread principal: %d\n”, global);
return 0;
}

void *thr_func(void *arg)
{
global = 40;
printf(“Novo thread: %d\n”, global);
return NULL;
}

Exercicios

1.   O que é uma thread e quais as vontagens em sua utilização?

2.   Quais as vantagens e desvantagens do compartilhamento do espaço de enderaçamento entre threads de um mesmo processo?

3.   Dê exemplos do uso de threads no desenvolvimento de aplicativos.

4.   Quais os benefícios do uso de threads em ambientes cliente-servidor?

5.   Escreva um programa em java que faça uso de duas threads para somar os elementos de um vetor de inteiros e calcular a média de maneira colaborativa. Cada thread deve gerar a média de metade   dos   elementos.  Us váriáveis   de   memória   compartilhada   e   monitores   para sincronização das tarefas.

Fonte: http://sites.google.com/site/jmlaine/so - http://docs.google.com/viewer?a=v&pid=sites&srcid=ZGVmYXVsdGRvbWFpbnxqbWxhaW5lfGd4OjUwNGIxNTFmZDAwOTIzNWM

Nenhum comentário:

Postar um comentário

Sejam bem vindos!


Espaço destinado às novidades sobre o mundo da tecnologia, experiências (idéias, protótipos, soluções, dentre outros), jogos e principalmente materiais de TI (apostilas, artigos, projetos, provas, etc).

A maioria do material postado aqui será de tutoriais garimpados ou comprados na net, e terá um toque feminino (o meu, é claro :-D), mas podem ficar tranquilos, todos terão os devidos créditos.


Viu algum material seu e não teve crédito? Avise!!! "Creditarei" com o maior prazer.