Este artigo engloba todas as funcionalidades das threads dentro do sistema operacional e dentro de futuros softwares que vocês desejarem construir. Aqui usarei exemplos em Python para poder demonstrar a funcionalidade das threads dentro do nosso dia a dia do desenvolvimento.
Threads em inglês significa linha de execução, é uma forma de um processo dividir a si mesmo em duas ou mais tarefas que podem ser executadas concorrentemente (fonte: Wikipédia)
Um pouco de história
Dentro de um sistema operacional temos as funções de threads em nosso dia a dia, hoje em dia todos os SOs trabalham com multi-threads, pois quem se lembra do antigo MS-DOS ou das primeiras versões do GNU/Linux sabe que não era possível executar mais de uma tarefa ao mesmo tempo, já que qualquer tarefa executada dentro do SO tomava 100% do processador. Após alguns anos foi visto que muitas aplicações não tomavam 100% do processamento do processador, isso era uma grande perda de tempo dentro dos SOs. Até que foi criada a ideia de multi-threads, onde se dividiam as linhas de execução dentro do SO aumentando sua eficiência.
Não apenas nos SOs temos threads. Processadores Intel também possuem threads para aumentar sua eficiência. Os processadores Pentium HT foram os primeiros da Intel a terem multi-threads implementados onde seu SO entendia que havia dois processadores, mas na verdade havia um com duas threads, que auxiliavam na execução do seu SO e aplicações.
Threads no seu dia-a-dia
Muitos desenvolvedores iniciantes nem sabem o que são threads e muito menos sabem qual a sua função dentro de um software. Isso é muito ruim, pois se temos um jovem programador que compra um Quad-core da Intel achando que sua aplicação vai funcionar mais rápido pois vai trabalhar com quatro núcleos ao invés de um está completamente errado. Pois se o programador desenvolver um software usando orientação a objeto ou uma linguagem estruturada muito complexo, onde tem que fazer, por exemplo, 40 comparações entre strings gigantescas e para isso usam uma classe ou uma função onde após comparar uma vai para a próxima e assim sucessivamente. Não adiante ter um dual - core, quad-core ou um octa-core, pois o problema está na forma do desenvolvimento.
Para um problema como esse você deve usar o máximo de seu processador, caso você resolva fazer isso em C sem usar nenhuma forma de multi-thread, ele vai usar apenas um núcleo de seu super computador. E assim todo o dinheiro investido em um processador muito eficiente não valeu de nada. Neste caso o ideal para um quad-core seria o programador usar pelo menos 4 threads para esse problema. Pois o SO iria ser o responsável por enviar os dados para os núcleos do processador. Assim aumentando a eficiência do software, sendo assim iria aumentar a eficiência em pelo menos quatro vezes.
Esta forma de programação é chamada de programação concorrente, onde temos um software que processa dados em várias threads otimizando o resultado final.
E também, em C++, quando sair a nova versão do padrão, threads vai ser parte da linguagem, algo padronizado que deverá funcionar em todos os compiladores, e o método de se trabalhar com elas não é nem orientado a classes e parecido com o POSIX threasd, mas é bem interessante e usa templates: http://en.wikipedia.org/wiki/C%2B%2B0x#Threading_facilities
[3] Comentário enviado por salviof em 26/08/2010 - 08:48h
Não adianta nada o sistema ser multprocessado no caso do windows, o windows separa a execução do executavel por nucleo, e você pode ter mil Threads rodando ao mesmo tempo que elas vão ser executadas em um único processador, e não dividida entre os outros processadores...
Me corrija se eu estiver errado, mas em testes verifiquei que mesmo com muitas threads o maximo que consegui ocupar foi 1 núcleo....
Mas a utilizaçõa de threads é essencial para não travar a tela do sistema enquanto estiver executando uma tarefa pesada (com o aviso: este programa não está respondendo), ou se precisar executar várias tarefas ao mesmo tempo....
[4] Comentário enviado por TRBaldim em 26/08/2010 - 09:10h
Salviof,
Sim, esta forma que apresentei no artigo ela usa o mesmo núcleo... Há várias formas de aplicar threads dentro de vários núcleos. No caso de Python ainda não sei como fazer. Mas temos uma liguagem chamada ADA que ela pode ser aplicada aos vários núcleos. C também pode ser aplicado a vários núcleos. Acho que Java também dá para fazer. Irei pesquisar mais sobre Python mas ótima observação.
[5] Comentário enviado por stremer em 26/08/2010 - 14:29h
amigo, desculpa mas acho que esta meio confuso...
uma coisa é thread outra é multi-task...
pelo que eu saiba thread existe a muito tempo
e no ms-dos já tinhamos programas multi-thread. (não threads posix como falado abaixo, mas programas capazes de realizar mais de uma tarefa ao mesmo tempo, um tipo de thread simulada, mas não como processo pois não havia isolamento também como falado)
Agora multi-task é outra coisa, embora possivel também no dos com softwares de terceiros (que faziam uma série de gambis internas).
Multi-task é coisa do sistema operacional, rodar varios processos em paralelo.
Cada processo pode possuir uma ou muitas tarefas (chamadas também de threads mas não é só com elas que conseguimos paralelismo).
O sistema operacional que também deve ser compativel com multi-processamento, e mesmo que o programa seja o mais burro para rodar em apenas um nucleo, o sistema operacional tem que fazer muito mais coisa para justificar varios nucleos...
Agora o programa multi-thread rodar em varios nucleos é um pouco mais complicado (outra coisa estranha na sua explicação que cita que se o programador não fizer multi-thread de nada adianta ter 2 ou mais nucleos), neste caso, sei que o java consegue fazer isso com o parâmetro -XX:+UseParallelGC neste caso o garbage collector esta implementado para rodar em mais nucleos tornando o desempenho do programa melhor como um todo já que a VM é responsavel por grande parte da execução do programa.
Ou seja, isso também NÃO É LIMITAÇÃO DO WINDOWS e SIM DO PROGRAMA!
[6] Comentário enviado por stremer em 26/08/2010 - 14:47h
resumindo: lendo agora parece que ficou meio confuso: De nada adianta o programa ser multi-thread se dentro dele as partes pesadas forem sequenciais...
por isso um processo pode ter 20 threads e estar utilizando apenas 50% do cpu... pois esta preso em um processo sequencial...
com o java, usando o parallelGC, como a VM é responsavel por alocação/desalocação de memória, toda esta parte multi-thread acaba utilizando mais nucleos, pois senão acaba dando gargalo na VM...
[7] Comentário enviado por stremer em 26/08/2010 - 15:08h
PS: No MS-DOS... as threads eram meio que "simuladas" no programa... pois o sistema nativamente suportava apenas um processo (não era multi-task) e não suportava threads nativas....
haviam bibliotecas que faziam isso.
A mesma coisa paara multi-task existia o desqview...
[8] Comentário enviado por TRBaldim em 26/08/2010 - 15:21h
Vamos lá, quando eu falo em usar um programa em threads estamos falando em executar várias tarefas ao mesmo tempo. Crie um programa que compacta 10 arquivos ao mesmo tempo. Você vai se você não usa threads você terá que fazer uma a um dentro. Agora você fazendo threads você pode aplicar em várias tarefas simultâneas. Agora se o WINDOW$ não consegue fazer isso... Eu já não sabia... Não entendo muito da funcionalidade dele... Mas vamos falar de um SO que trabalha MUITO bem com vários processadores e vários núcleos. Como eu conheço bem Linux e Unix digo que eles podem redistribuir as aplicações dentro dos nucleos ou processadores. Não sabia que Windows era tão burro a ponto de não saber fazer nada disso... Mas que seja e o seu parâmetro -XX:+UseParallelGC só faz o garbage collector rodar em uma thread paralela ao seu programa para limpar a memória mais eficientemente... Mas a distribuição no processador não é responsabilidade da JVM mas sim do SO... A JVM trabalha diretamente com a memória... Ela não tem uma linguagem de máquina tão baixa assim... Em geral a forma de se programar em threads tem muitas e muitas e muitas... O que eu demonstrei foi como criar uma thread... Mas dentro as minhas pesquisas para eu poder ter mais de uma thread separadamente por processo tem que ser feito separadamente e com outras formas de se chamar o programa...
[9] Comentário enviado por stremer em 26/08/2010 - 16:24h
o problema que as vezes mesmo programando em paralelo, há outros gargalos que impedem que o SO redistribua.... por isso falei que não é tão simples assim....
por isso falei que a culpa não é do windows....
No caso do useparallelgc é uma forma de implementação do garbage que é própria para rodar em paralelo e como no java toda a alocação/desalocação é feita pela JVM, o funcionamento do garbage influencia diretamente na performance do programa...
[10] Comentário enviado por psfdeveloper em 26/08/2010 - 20:40h
Parece que o pessoal está confundindo um pouco multi-tasking e multi-threading. Vou tentar explicar o que eu aprendi a esse respeito...
Multi-tasking (multi-tarefa) é a capacidade de um sistema operacional executar mais de um programa simultaneamente. O multi-tasking pode ser cooperativo (o que quase não existe mais porque é obsoleto), quando é responsabilidade do próprio programa passar a execução para os outros ou preemptivo, quando o sistema operacional controla quanto de processamento cada programa vai utilizar dependendo de sua prioridade dentro de uma estrutura pertencente ao sistema operacional chamada de "scheduler".
Multi-threading é simplesmente uma forma de multi-tasking. Existem dois tipos de processos, os heavyweight processes, que são os nossos velhos conhecidos programas, que consistem de uma linha de execução, a thread, em um espaço de memória reservado apenas a ela. Isso é chamado de Process Isolation. Um programa POSIX pode criar novos heavyweight processes usando a syscall fork.
E existem os lightweight processes, que são várias linhas de execução, ou várias threads, compartilhando o mesmo espaço de memória, ou seja, dentro do mesmo programa. Esse último caso (lightweight process) é o que ficou convencionalmente sendo chamado de programas com multi-threading, o que não é uma convenção enganosa. Para se criar threads, pode-se usar a rotina pthread_create do da biblioteca POSIX Threads. pthread_create é uma função da biblioteca C do Linux (e de vários outros Sistemas Operacionais Unix Like - incluindo o Mac OS X!). A criação de Threads a partir de Syscalls do Linux não é trivial e a forma padrão de se criar programas multi-thread no linux é via POSIX Threads.
Geralmente o sistema operacional distribui as threads entre os processadores, mas duas coisas podem impedir tal distribuição. A primeira é o uso de recursos de sistema compartilhados como Locks e outras ferramentas de sincronização. Outra possibilidade é a existência de algum programa externo rodando paralelamente e executando em alta prioridade, o que leva o sistema operacional a reservar um núcleo só para ele. Uma sincronização bem feita permite o compartilhamento de recursos entre threads e a distribuição do processamento entre núcleos, mas isso não é fácil de se fazer. A melhor forma de atingir esse objetivo é através do isolamento de recursos compartilhados em outro processo (como um banco de dados) que faz a sincronização, enquanto as threads rodam totalmente independente umas das outras.
Espero que eu tenha conseguido dar alguma dica proveitosa. Abraços!
P.S.: Eu programei muitos, muitos anos, para Windows, e não me lembro de ter problemas com distribuição de processos entre núcleos de processadores. Acho interessante sermos isentos quanto às nossas posições em relação a outros sistemas ou produtos porque eles podem ter coisas muito boas que podemos usar como modelo para nossos próprios trabalhos. Mesmo o Windows pode ter alguma coisa para contribuir para a gente.
[11] Comentário enviado por stremer em 26/08/2010 - 23:34h
psfdeveloper....
falou tudo... era nisso que queria chegar, mas realmente tenho uma dificuldade impressionante para tentar explicar algumas coisas...
agora vc fez lembrar do cooperativo e preemptivo.... no dos era cooperativo qdo se implementava em aplicativos win31 por exemplo....
qto ao windows... depende mesmo dos recursos compartilhados, por isso as vezes nao usa os nucleos mesmo programando em 2 ou mais threads...
[12] Comentário enviado por psfdeveloper em 27/08/2010 - 06:55h
Muito obrigado pelo comentário, stremer.
Eu me lembro do Win31 e do DOS, também, afinal, foi nesses sistemas que eu comecei a programar. Quando o Windows95 veio com a multitarefa preemptiva, eu lembro que quase chorei de tanto que fiquei fascinado com a idéia. Hoje, multitarefa preemptiva existe até em sistemas operacionais embarcados e em telefones celulares (como no caso do Symbian, por exemplo).
Multi-threading é um assunto tão fascinante que os iniciantes, quando começam a trabalhar com elas, acham que se trata de uma panacéia e que tudo pode ser feito com Threads. Eu mesmo passei por isso. Teve uma época que eu queria fazer tudo com multitarefa. Mas aí vieram os compartilhamentos de recursos, os locks, as operações ilegais (no windows) os segmentation faults e eu percebi que programar multitarefa é espinhoso.
Linguagens como Python e, também o Java, tornam a criação de código multi-tarefa quase trivial - o que é simplesmente maravilhoso! Quando eu comecei, tínhamos três opções: Pascal, C e Assembly. Nem mesmo os compiladores C++ eram bons o suficiente - e não existiam bibliotecas decentes para multitarefa. Eu me lembro do compilador da Microsoft sem suporte a Templates (!) e com uma infinidade de bugs, ainda por cima.
Mas devemos lembrar sempre: um programa mono-tarefa bem feito, como, por exemplo, uma máquina de estados para controle de eventos, pode ser até mais eficiente, econômico em recursos de sistema e fácil de ler e entender do que a sua contrapartida multitarefa. Devemos lembrar: threads são um assunto avançado e merecem respeito. O Python torna fácil manipular as Threads, mas ele não faz com que as threads se comportem como queremos: ainda estamos no controle. Afinal, os engenheiros, analistas ou programadores somos nós, não a linguagem.
Threads são maravilhosas, mas devem ser usadas criteriosamente - e com parcimônia!
[13] Comentário enviado por stremer em 27/08/2010 - 10:24h
exato... não é necessário usar thread para implementar tarefas em paralelo.
Threads são complexas embora aparentemente simples. Seu uso sem critério pode ter graves consequências, como por exemplo saturação de recursos do sistema.
Se fosse tão simples, "roda em paralelo, cria-se thread", não existiria thread pool (apenas um exemplo).
E como sempre, analise caso a caso antes de sair implementando determinada solução.
Mesmo assim os exemplos do thread no python estão legais.