Sunteți pe pagina 1din 15

Thread (cincia da computao)

Nota: Para outros significados de thread, veja thread (desambiguao). Linha ou Encadeamento de execuo (em ingls: Thread), uma forma de um processo dividir a si mesmo em duas ou mais tarefas que podem ser executadas concorrentemente. O suporte thread fornecido pelo prprio sistema operativo (SO), no caso da linha de execuo ao nvel do ncleo (em ingls: Kernel-Level Thread (KLT)), ou implementada atravs de uma biblioteca de uma determinada linguagem, no caso de uma User-Level Thread (ULT). Uma thread permite, por exemplo, que o usurio de um programa utilize uma funcionalidade do ambiente enquanto outras linhas de execuo realizam outros clculos e operaes. Em hardwares equipados com uma nica CPU, cada thread processada de forma aparentemente simultnea, pois a mudana entre uma thread e outra feita de forma to rpida que para o utilizador isso est acontecendo paralelamente. Em hardwares com mltiplos CPUs ou multi-cores, as threads so realizadas realmente de forma simultnea; Os sistemas que suportam apenas uma nica thread (em real execuo) so chamados de monothread enquanto que os sistemas que suportam mltiplas threads so chamados de multithread.

Exemplo
Um exemplo simples pode ser expressado atravs de um jogo onde o mesmo pode ser modelado com linhas de execuo diferentes, sendo uma para desenho de imagem e outra para udio; Neste caso, h um thread para tratar rotinas de desenho e outro thread para tratar udio; No ponto de vista do usurio, a imagem desenhada ao mesmo tempo em que o udio emitido pelos alto-falantes; Porm, para sistemas com uma nica CPU, cada linha de execuo processada por vez;

Particularidades
Cada thread tem o mesmo contexto de software e compartilha o mesmo espao de memria (endereado a um mesmo processo-pai), porm o contexto de hardware diferente. Sendo assim o overhead causado pelo escalonamento de uma thread muito menor do que o escalonamento de processos. Entretanto no h acesso protegido memria nativa (sua implementao fica a cargo do programador) devido ao compartilhamento do espao de memria.

Um dos benefcios do uso das threads advm do facto do processo poder ser dividido em vrias threads; quando uma thread est espera de determinado dispositivo de entrada/sada ou qualquer outro recurso do sistema, o processo como um todo no fica parado, pois quando uma thread entra no estado de 'bloqueio', uma outra thread aguarda na fila de prontos para executar. Uma thread possui um conjunto de comportamentos padro, normalmente encontrados em qualquer implementao ou sistema operativo. Uma thread pode:

criar outra da mesma forma que um processo, atravs do mtodo thread-create, onde a thread retorna um ID como primeiro argumento (resultado da funo de criao); esperar outra thread se sincronizar, atravs do mtodo join; voluntariamente "desistir" da CPU por no precisar mais do processamento proposto pela prpria ou por vontade do utilizador. Feito atravs do mtodo thread-yield; replicar-se sem a necessidade de duplicar todo o processo, economizando assim memria, processamento da CPU e aproveitando o contexto (variveis, descritores, dispositivos de I/O).

Estados de uma linha de execuo


Uma thread pode assumir os seguintes estados:

Unstarted: logo aps ser criada (antes do Start()); Running: aps ser ativada (Start()) ou aps mtodo Resume(); Suspended: aps mtodo Suspended(); Stopped: aps mtodo Abort().

ULT e KLT
Usualmente as threads so divididas em duas categorias: thread ao nvel do utilizador (em ingls: User-Level Thread (ULT)), e thread ao nvel do ncleo (em ingls: KernelLevel Thread (KLT)). Thread em modo usurio

Thread em modo kernel

As threads da primeira categoria (ULT) so suportadas pela aplicao, sem conhecimento do ncleo e geralmente so implementadas por pacotes de rotinas (cdigos para criar, terminar, escalonamento e armazenar contexto) fornecidas por uma determinada biblioteca de uma linguagem, como o caso da thread.h (biblioteca padro da linguagem C). Estas threads suportam as mesmas operaes que as threads KLT (criar, sincronizar, duplicar e abortar). Possuem como vantagens a possibilidade de

implementao em sistemas operativos que no suportam nativamente este recurso, sendo geralmente mais rpidas e eficientes pois dispensam o acesso ao ncleo. Evita assim mudana no modo de acesso, e a estrutura de dados fica no espao do utilizador, levando a uma significativa queda de overhead, alm de poder escolher entre as diversas formas de escalonamento em que melhor se adequa. A gesto da thread (KLT) no realizada atravs do cdigo do prprio programa; todo o processo subsidiado pelo SO. Esse modelo tem a vantagem de permitir o suporte a multiprocessamento e o facto do bloqueio de uma linha de execuo no acarretar bloqueio de todo processo, no obstante, temos a desvantagem de ter que mudar o tipo de acesso sempre que o escalonamento for necessrio, aumentando assim o to temido overhead. H quatro operaes bsicas na gesto de threads: criar, terminar, thread join e thread yield. Criar (thread creation) Basicamente uma thread pode criar outra(s), sendo que depois essas mesmas threads so executas 'simultaneamente'. A thread criadora a thread-me e a thread criada a thread-filho. Threads includas na funo main quando executadas podem criar threadsfilho. No diagrama a seguir h a thread A que executa inicialmente. Mais tarde criada a thread B indicada no ponto amarelo. Depois de criadas, a thread A e thread B executam simultaneamente. Em seguida a thread A pode criar uma ou mais threads (por exemplo uma thread C). Depois de criada a thread C, h trs threads executando simultaneamente e todas disputam o uso da CPU. Entretanto, a thread que pode ser executada a qualquer momento no de conhecimento da CPU. Terminar (thread termination) Para maioria dos casos, as threads no so criadas e executadas eternamente. Depois de terminado o seu objectivo, a thread termina. No facto, a thread que criou estas duas threads-filho termina tambm, porque sua tarefa atribuda se completa. Na matrix de multiplicao (matrix multiplication), uma vez que o valor de C[i,j] computado, a thread correspondente termina. Em geral quando a tarefa atribuda a thread completa, a thread pode ser terminada. Alm disso, se a thread-me terminar, todas as threads filho terminam tambm. Porque isso importante? Isso importante porque as threads-filho compartilham recursos com a thread-me, incluindo variveis. Quando a thread-me termina, todas as variveis so perdidas e a thread-filho no poder aceder aos recursos que a thread-me possuia. Assim, se a thread-me terminar mais cedo que a thread-filho haver um problema! Uma thread pode terminar das seguintes maneiras:

Retornando da sua rotina mais externa, a thread criadora. Quando termina a rotina em que foi comeada. Chamando pthread_exit, fornecendo um estado de sada. Terminando atravs da funo pthread_cancel

Sincronizar(Thread Join)

Imagine a seguinte situao: Voc est estudando para uma prova. Ento voc pede o seu irmo mais novo para comprar uma pizza. Neste caso voc a thread principal e seu irmo a thread-filho. Uma vez que voc deu a ordem, voc e seu irmo comeam a executar uma tarefa simultaneamente. Agora h dois casos a se considerar: Primeiro: Seu irmo traz a pizza e termina enquanto voc estuda. Nesse caso voc pode parar de estudar e comer a pizza. Segundo: Voc acaba de estudar mais cedo e dorme e depois a pizza chegar. A juno de threads (thread join) destinada para resolver este problema. A thread pode executar o thread join e aguardar at a outra thread terminar. No caso acima, voc a thread principal (thread main) e deve executar o thread join aguardando o seu irmo (thread-filho) terminar. Em geral o thread join utilizado para a thread-me se sincronizar com uma das threads-filho. Thread Yield (Rendimento da thread) Suponha que voc executa um certo nmero de programas o tempo todo no computador. Isso possvel devido a CPU escalonar pouco a pouco outros ciclos da CPU, assim outros programas podem ser executados. Isso pode ser um problema de poltica de planeamento do Sistema Operativo. Entretanto, quando escrevemos programas com mltiplas threads, temos que fazer correctamente para que algumas threads no ocupem a CPU eternamente, ou por um tempo muito longo sem abandonlo. Seno terminar na situao acima quando uma ou duas threads executam enquanto outras simplesmente esperam para retornar. Liberamos espao na memria graas a thread yield. Quando a thread executa o thread yield, a execuo da thread suspensa e a CPU passa para uma outra thread em execuo. Essa thread aguardar at a CPU tornar-se disponvel novamente.

Escalonamento
Da mesma forma que os processos sofrem escalonamento, as threads tambm tm a mesma necessidade. Quando vrios processos so executados em uma CPU, eles do a impresso que esto sendo executados simultaneamente. Com as threads ocorre o mesmo, elas esperam at serem executadas. Como esta alternncia muito rpida, h impresso de que todas as threads so executadas paralelamente. Linha de execuo ao nvel do usurio As ULT so escalonadas pelo programador, tendo a grande vantagem de cada processo usar um algoritmo de escalonamento que melhor se adapte a situao, o sistema operacional neste tipo de thread no faz o escalonamento, em geral ele no sabe que elas existem. Neste modo o programador responsvel por criar, executar, escalonar e destruir a thread. Um exemplo prtico de processo chamado P1 que contm tais threads: P1T1, P1T2 e P1T3, quando o sistema operacinal da a CPU para o processo P1 cabe a ele destinar qual thread ser executada, caso esta thread use todo processo do quantum, o sistema operacional chamar outro processo, e quando o processo P1 voltar a

executar, P1T1 voltar a ser executada e continuar executando at seu trmino ou interveno de P1, este comportamento no afetar outros processos pois o sistema continua escalonando os processos normalmente. Linha de execuo ao nvel do ncleo As KLT so escalonadas diretamente pelo sistema operacional, comumente so mais lentas que as Threads ULT pois a cada chamada elas necessitam consultar o sistema, exigindo assim a mudana total de contexto do processador, memria e outros nveis necessrios para alternar um processo. Um exemplo prtico de processo chamado P2 que contm as threads P2T1, P2T2 e P2T3 e um processo chamado P3 que contm as threads P3T1, P3T2 E P3T3. O Sistema Operacional no entregar a CPU ao processo e sim a uma thread deste processo, note agora que o sistema responsvel por escalonar as threads e este sistema tem que suportar threads, a cada interrupo de thread necessrio mudar todo o contexto de CPU e memria, porm as threads so independentes dos processos, podendo ser executadas P3T2, P2T1, P2T2, P2T1, P3T1,P2T3,P3T3, ou seja a ordem em que o escalonador do sistema determinar. J com as threads em modo usurio no se consegue ter a mesma independncia, pois quando passamos o controle ao processo, enquanto seu quantum for vlido ele ir decidir que thread ir rodar. Um escalonamento tpico do sistema onde o escalonador sempre escolhe a thread de maior prioridade, que so divididas em duas classes: Real Time e Normal. Cada thread ganha uma prioridade ao ser criada que varia de 0 a 31(0 a menor e 31 maior), processos com prioridade 0 a 15(Real Time) tem prioridade ajustada no tempo de execuo como nos processos de E/S que tem a prioridade aumentada variando o perifrico, processos com prioridade 16 a 31 so executados at terminar e no tem prioridade alterada, mas somente uma thread recebe a prioridade zero que a responsvel por zerar pginas livres no sistema. Existe ainda uma outra classe chamada de idle, uma classe mais baixa ainda, s executada quando no existem threads aptas, threads dessa classe no interferem na performance.

Comparao entre linha de execuo e Processo


Um sistema baseado em linha de execuo diferente de um sistema operacional multitarefa tradicional, em que processos so tipicamente independentes, carregam considervel estado da informao, tem endereo de memria separado e interagem somente atravs de mecanismos de inter-processos de comunicao. As threads, por outro lado, compartilham o estado da informao de processos nicos, e compartilham memria e outros recursos diretamente. A troca de contexto atravs de linha de execuo num mesmo processo tipicamente mais rpida que a troca de contexto entre processos diferentes. Sistemas como o Windows NT e o OS/2 so feitos para ter linha de execuo "baratas" e processos "caros", enquanto em outros sistemas operacionais no h grandes diferenas. O multithreading um modelo de programao popular que permite a execuo de mltiplas linha de execuo dentro de um contexto simples, compartilhando recursos do

processo, e capazes de executar de forma independente. O modelo de programao em linha de execuo fornece ao desenvolvedor uma execuo simultnea. Entretanto, a aplicao mais interessante da tecnologia ocorre quando ela utilizada em um processo simples permitindo uma execuo paralela em sistemas multi-processados. Um sistema multi-threaded possui um melhor desempenho que um sistema de computadores com mltiplas CPUs e com mltiplos ncleos, ou que um cluster de mquinas. Isto acontece porque a linha de execuo empresta a ela mesmo uma execuo simultnea. Em alguns casos, o programador precisa ter cuidado em evitar condies de concorrncia e outros comportamentos inesperados. Para um dado ser manipulado corretamente, as linhas de execuo freqentemente precisaro ser sincronizadas, para que os dados sejam processados na ordem correta. As linha de execuo podem tambm executar operaes atmicas (freqentemente implementadas usando semforos) com intuito de prevenir que dados comuns sejam simultaneamente modificados ou lidos enquanto o processo esta sendo modificado. Os sistemas operacionais implementam as linhas de execuo de duas formas: multithreading preemptiva ou multithreading cooperativa. A multithreading preemptiva geralmente considerada uma implementao superior, porque permite ao sistema determinar quando uma troca de contexto pode acontecer. A multithreading cooperativa, por outro lado, confia nas threads para ceder o controle, o que pode ser um problema caso uma tarefa monopolize o uso da CPU ou se houver espera pela disponibilidade de um recurso. A desvantagem da multithread preemptiva que o sistema pode fazer uma troca em um tempo inapropriado, causando uma inverso de prioridade ou outros efeitos ruins que podem ser evitados por uma multithreading cooperativa. Em geral:

Criar um processo pode ser caro em termos de tempo, memria, e sincronizao entre processos. As linhas de execuo podem ser criadas sem a replicao do processo inteiro. O trabalho de criar uma linha de execuo pode ser feito no espao do usurio. Como as linhas de execuo partilham o espao de endereamento a comunicao entre elas mais rpida. O tempo gasto para troca de linha de execuo menor, em parte por que no h necessidade de troca de espao de endereamento.

Modelo de Gerao de Multithreads


Modelo Muitos-Para-Um O modelo muitos-para-um mapeia muitos threads de nvel de usurio para threads do kernel. O gerenciamento dos threads realizado no espao do usurioe assim eficiente, mas o processo inteiro ficar bloqueado. Alm disso, como somente um

thread pode acessar o kernel de cada vez, mltiplos threads so incapazes de executar em paralelo em multiprocessadores.[1] Modelo Um-Para-Um O modelo um-para-um mapeia cada thread de usurio para um thread de kernel, gera mais concorrncia do que o modelo muitos-para-um. Permite a um outro thread ser executado, enquanto um thread realiza uma chamada de sistema de bloqueio, ele tambm permite que mltiplos threads executem em paralelo em multiprocessadores. A nica desvantagem deste modelo que a criao de um thread de usurio requer a criao do correspondente thread de kernel.[1] Modelo Muitos-Para-Muitos O modelo muitos-para-muitos multiplexa muitos threads de nvel de usurio para um nmero menor ou igual de threads de kernel. O nmero de threads de kernel pode ser especfico tanto para uma aplicao em particular quanto para uma mquina em particular. Os desenvolvedores podem criar tantos threads de usurio quantos forem necessrios, e os correspondentes threads de kernel podem executar em paralelo em um multiprocessador. Alm disso, quando um thread realiza uma chamada de sistema de bloqueio, o kernel pode agendar um outro thread para execuo.[1]

Cancelamento
O cancelamento de threads corresponde tarefa de terminar um thread antes que se complete. Por exemplo, se mltiplos threads esto pesquisando concorrentemente em um banco de dados e um thread retorna o resultado, os threads que ainda esto sendo executados podem ser cancelados. Uma outra situao pode ocorrer quando um usurio pressionar um boto em um navegador da Web. Com frequncia, uma pgina da Web carregada em um thread separado. Quando um usurio pressionar o boto stop, o thread que

Exemplos

Java
import java.io.*; public class Example implements Runnable {

static Thread threadCalculate; // Cria o thread. static Thread threadListen; long totalPrimesFound = 0; public static void main (String[] args) { Example e = new Example(); threadCalculate = new Thread(e); threadListen = new Thread(e); threadCalculate.start(); threadListen.start(); } public void run() { Thread currentThread = Thread.currentThread(); if (currentThread == threadCalculate) calculatePrimes(); else if (currentThread == threadListen) listenForStop(); } public void calculatePrimes() { int n = 1; while (true) { n++; boolean isPrime = true; for (int i = 2; i < n; i++) if ((n / i) * i == n) { isPrime = false; break; } if (isPrime) { totalPrimesFound++; System.out.println(n); } } } private void listenForStop() { BufferedReader input = new BufferedReader(new InputStreamReader(System.in)); String line = ""; while (!line.equals("stop")) { try { line = input.readLine(); }

catch (IOException exception) {} } System.out.println("Found " + totalPrimesFound + " prime numbers before you said stop"); System.exit(0); } }

Java, exemplo simples em portugus


import java.util.logging.Level; import java.util.logging.Logger; class Threaded extends Thread { Synchronized1 base; public Threaded( Synchronized1 bse ) { this.base = bse; } } public class Synchronized1 { public Synchronized1() { } public void ini() { new Threaded( this ) { public void run() { while( true ) { synchronized( base ) { System.out.print( "Este A, agora vai mostrar B.\n" ); try { Thread.sleep(1000); } catch (InterruptedException ex) { Logger.getLogger(Synchronized1.class.getName()).log(Level.SEVERE, null, ex); } try { base.notify(); base.wait(); } catch (InterruptedException ex) { Logger.getLogger(Synchronized1.class.getName()).log(Level.SEVERE, null, ex); } } } } }.start();

new Threaded( this ) { public void run() { while( true ) { synchronized( base ) { System.out.print( "Este B, ento foi j mostrado A.\n" ); try { Thread.sleep(1000); } catch (InterruptedException ex) { Logger.getLogger(Synchronized1.class.getName()).log(Level.SEVERE, null, ex); } try { base.notify(); base.wait(); } catch (InterruptedException ex) { Logger.getLogger(Synchronized1.class.getName()).log(Level.SEVERE, null, ex); } } } } }.start(); } public static void main(String[] args) { new Synchronized1().ini(); } }

C Notas

Esta implementao depende do uso da biblioteca POSIX Threads.

#include <stdio.h> #include <stdlib.h> #include <pthread.h> #define THREADS_MAX 4

void *function(void *param) { int id = (int)param; int i, loops = 10; for(i = 0; i < loops; i++)

{ printf("thread %d: loop %d\n", id, i); } pthread_exit(NULL); } int main(void) { pthread_t threads[THREADS_MAX]; int i; printf("pre-execution\n"); for (i = 0; i < THREADS_MAX; i++) { pthread_create(&threads[i], NULL, function, (void *)i); } printf("mid-execution\n"); for (i = 0; i < THREADS_MAX; i++) { pthread_join(threads[i], NULL); } printf("post-execution\n"); return EXIT_SUCCESS; }

C++ Notas

Esta implementao depende do uso da biblioteca Boost.

#include <iostream> #include <boost/thread/thread.hpp> using namespace std; const int THREADS_MAX = 4; struct function { function( const int &_id ) : id( _id ) { } void operator()() { for( int i = 0; i < 10; ++i ) { cout << "thread " << id << ": loop " << i << endl; } }

private: const int id; }; int main(void) { boost::thread_group threads; cout << "pre-execution" << endl; for( int i = 0; i < THREADS_MAX; ++i ) { threads.create_thread( function( i ) ); } cout << "mid-execution" << endl; threads.join_all(); cout << "post-execution" << endl; return 0; }

Ruby
count = 0 a = Thread.new { loop { count += 1 } } sleep(0.1) Thread.kill(a) puts count #=> 93947

Delphi
unit UExemplo; interface uses Classes, Generics.Collections; type TThreadExemplo = class(TThread) private FPrimeiroNumero: Integer; FUltimoNumero: Integer; FListaPrimos : TList<Integer>; function IsPrimo(const pNumero : Integer) : Boolean; protected procedure Execute; override; public constructor Create(const pCreateSuspended: Boolean; const pPrimeiroNumero, pUltimoNumero: Integer); destructor Destroy; override;

property PrimeiroNumero: Integer read FPrimeiroNumero write FPrimeiroNumero; property UltimoNumero: Integer read FUltimoNumero write FUltimoNumero; function GetListaAsString: String; end; implementation uses SysUtils; { TThreadExemplo } constructor TThreadExemplo.Create(const pCreateSuspended: Boolean; const pPrimeiroNumero, pUltimoNumero: Integer); begin inherited Create(pCreateSuspended); FListaPrimos := TList<Integer>.Create; FPrimeiroNumero := pPrimeiroNumero; FUltimoNumero := pUltimoNumero; end; destructor TThreadExemplo.Destroy; begin FListaPrimos.Free; inherited; end; procedure TThreadExemplo.Execute; var lNumero: Integer; begin lNumero := FPrimeiroNumero; while not Terminated and (lNumero <= FUltimoNumero) do begin if IsPrimo(lNumero) then begin FListaPrimos.Add(lNumero); end; Inc(lNumero); end; end; function TThreadExemplo.GetListaAsString: String; var lNum: Integer; begin Result := EmptyStr; for lNum in FListaPrimos do begin Result := Result + IntToStr(lNum) + sLineBreak; end; end;

function TThreadExemplo.IsPrimo(const pNumero: Integer): Boolean; var lNum: Integer; lMax: Integer; begin Result := True; lNum := 2; lMax := (pNumero div 2); while Result and not Terminated and (lNum <= lMax) do begin Result := pNumero mod lNum <> 0; Inc(lNum); end; end; end.

Referncias
1. a b c d SILBERSCHATZ, Abraham; GALVIN, Peter Baer; GAGNE, Greg. Fundamentos de sistemas operacionais. 6. ed. Rio de Janeiro: LTC, 2004 580 p.

S-ar putea să vă placă și