Python com Threads


Hoje em dia, os novos processadores estão com cada vez mais núcleos. Começou no HT, depois 2 núcleos, 3 núcleos, 4 núcleos, e por ai vai…

Por exemplo, aqui na empresa tem uma maquina de 64bits com 8 processadores, mas que naverdade são 2 processadores fisicos com 4 núcleos cada um.

E a indagação que os programas não estão preparados para utilizar todo o poder de processamento das novas máquinas, com vários núcleos. Isto é verdade ? Não sei bem, depende muito.

Acho que o que deve saber de fato utilizar o poder dos núcleos é o sistema operacional. Se este não souber, não há programa que consiga trabalhar bem, mesmo estando preparado. E convenhamos que o nosso kernel linux ( e outros *nix ) sabem fazer muito bem!

E que aplicativos devem se preparar para utilizar tarefas multi processadas ? Eu acredito que aquelas mais pesadas, ou nem tanto, mas que não dependam de intervenção do usuário, como por exemplo: um banco de dados, um processamento em batch de indexação, geração de imagens, processamento de vídeos, e por ai vai. Um browser poderia se utilizar de multi-threads também, como faz por exemplo o Chrome. Agora convenhamos, que o nosso GEdit da vida, onde digitamos apenas alguns códigos e/ou textos, não necessita ser multi-thread. Correto ! hehehe

Bom, mas para nós que programamos em python, tem algumas soluções em multi-thread, e 1 delas eu vou mostrar aqui: o módulo “threading”

Podemos criar um simples programa com tarefas rodando em paralelo, que vai ser mostrado aqui, ou um deamon, que funciona mais ou menos como uma nave espacial: colocamos os astronautas, ligamos e mandamos para o espaço, e ela fica lá, orbitando nossas kbças… hahehaeh Mas este é outra história…

Então vamos lá.

– Criando uma Thread Simples:

#!/usr/bin/env python
# -*- coding: cp1252 -*-
import threading as t

class MyThread(t.Thread):
    def run(self):
        print "init thread " + self.getName() 

mt = MyThread()
mt.start()

print 'fim'

Aqui, a nossa thread simplesmente imprime o texto e sai fora. Detalhe, nunca confundam threads com agendadores, ou seja, a thread quando chamada, executa o método “run” e morre. Ela não fica executando este método de tempos em tempos, como as vezes achamos. Ok ! Para isto temos que controlar a execução da mesma.

– Criando uma Thread mais avançadinha:

#!/usr/bin/env python
# -*- coding: cp1252 -*-
import threading as t
import time

class MyThread(t.Thread):
    def __init__(self, c, d):
        t.Thread.__init__(self)
        self.count = c
        self.delay = d

    def run(self):
        print "init thread " + self.getName()
        ct = 0
        while ct < self.count:
            time.sleep(self.delay)
            ct += 1
            print "%s: %s\n" % ( self.getName(), time.ctime(time.time()) ) 

mt = MyThread(3,2)
mt.start()
#mt.join()

mt1 = MyThread(3,3)
mt1.start()
#mt1.join()

print 'fim'

Detalhe, cada sistema operacional trabalha de uma forma diferente com as threads e o gerenciamento de todas as threads do sistema. Por isto, trabalhar e controlar threads no seu programa/sistema pode não ser algo tão trivial. Teste sempre em ambos ambientes.

O nosso exemplo acima, criamos uma simples thread que imprime um texto no terminal. Porém nosso programa principal fica aguardando o retorno de ambas para poder terminar, mesmo não tendo mais instruções depois de “print ‘fim'”. Agora, neste mesmo exemplo, alterne o comentário nas linhas onde se chama o método “join” de cada thread depois de criada, e veja a diferença: ele faz com que fique aguardando o final do processamento da thread naquele ponto mesmo.

Este é um exemplo que tem na documentação do python mesmo, vejam que interessante:

import threading, zipfile

class AsyncZip(threading.Thread):
    def __init__(self, infile, outfile):
        threading.Thread.__init__(self)
        self.infile = infile
        self.outfile = outfile
    def run(self):
        f = zipfile.ZipFile(self.outfile, 'w', zipfile.ZIP_DEFLATED)
        f.write(self.infile)
        f.close()
        print 'Finished background zip of: ', self.infile

background = AsyncZip('mydata.txt', 'myarchive.zip')
background.start()
print 'The main program continues to run in foreground.'

background.join()    # Wait for the background task to finish
print 'Main program waited until background was done.'

E aproveitando até mesmo o texto que tem no tutorial, vou fazer um resumo:

Usar Threads é uma técnica utilizada para desacoplar tarefas que não sequencialmente dependentes. São usadas para aumentar a capacidade de resposta das aplicações enquanto se processa outros dados e/ou entradas do usuário. O principal desafio, entretanto, é coordenar o compartilhamento de variáveis e resources, e para isto o módulo disponibiliza ferramentas como Locks, Eventos, Semaforos  e variáveis de condição.

Depois agente pode ver alguma coisa sobre estes Locks e Semaforos, para entender melhor.

Por enquanto ficamos aqui.

Um abraço a todos ! E caso tenham alguma dica interessante, por favor, sinta-se a vontade para me enviar que com prazer eu adicionarei no post !

Links:

Anúncios

2 Comments

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s