Python - Programação Multithread
Executar vários threads é semelhante a executar vários programas diferentes simultaneamente, mas com os seguintes benefícios -
Vários threads dentro de um processo compartilham o mesmo espaço de dados com o thread principal e podem, portanto, compartilhar informações ou se comunicar uns com os outros mais facilmente do que se fossem processos separados.
Threads às vezes chamados de processos leves e não requerem muita sobrecarga de memória; eles são mais baratos do que processos.
Um thread tem um início, uma sequência de execução e uma conclusão. Ele tem um ponteiro de instrução que mantém o controle de onde dentro do contexto ele está sendo executado.
Pode ser antecipado (interrompido)
Ele pode ser temporariamente colocado em espera (também conhecido como hibernação) enquanto outros threads estão em execução - isso é chamado de rendição.
Iniciando um Novo Tópico
Para gerar outro thread, você precisa chamar o seguinte método disponível no módulo thread -
thread.start_new_thread ( function, args[, kwargs] )
Essa chamada de método permite uma maneira rápida e eficiente de criar novos threads no Linux e no Windows.
A chamada do método retorna imediatamente e o thread filho inicia e chama a função com a lista de argumentos passada . Quando a função retorna, o thread termina.
Aqui, args é uma tupla de argumentos; use uma tupla vazia para chamar a função sem passar nenhum argumento. kwargs é um dicionário opcional de argumentos de palavras-chave.
Exemplo
#!/usr/bin/python
import thread
import time
# Define a function for the thread
def print_time( threadName, delay):
count = 0
while count < 5:
time.sleep(delay)
count += 1
print "%s: %s" % ( threadName, time.ctime(time.time()) )
# Create two threads as follows
try:
thread.start_new_thread( print_time, ("Thread-1", 2, ) )
thread.start_new_thread( print_time, ("Thread-2", 4, ) )
except:
print "Error: unable to start thread"
while 1:
pass
Quando o código acima é executado, ele produz o seguinte resultado -
Thread-1: Thu Jan 22 15:42:17 2009
Thread-1: Thu Jan 22 15:42:19 2009
Thread-2: Thu Jan 22 15:42:19 2009
Thread-1: Thu Jan 22 15:42:21 2009
Thread-2: Thu Jan 22 15:42:23 2009
Thread-1: Thu Jan 22 15:42:23 2009
Thread-1: Thu Jan 22 15:42:25 2009
Thread-2: Thu Jan 22 15:42:27 2009
Thread-2: Thu Jan 22 15:42:31 2009
Thread-2: Thu Jan 22 15:42:35 2009
Embora seja muito eficaz para threading de baixo nível, o módulo de thread é muito limitado em comparação com o módulo de threading mais recente.
O Módulo de Threading
O módulo de threading mais recente incluído no Python 2.4 oferece suporte de alto nível muito mais poderoso para threads do que o módulo de thread discutido na seção anterior.
O módulo de threading expõe todos os métodos do módulo de thread e fornece alguns métodos adicionais -
threading.activeCount() - Retorna o número de objetos de thread que estão ativos.
threading.currentThread() - Retorna o número de objetos de thread no controle de thread do chamador.
threading.enumerate() - Retorna uma lista de todos os objetos de thread que estão atualmente ativos.
Além dos métodos, o módulo de threading possui a classe Thread que implementa o threading. Os métodos fornecidos pela classe Thread são os seguintes -
run() - O método run () é o ponto de entrada para um thread.
start() - O método start () inicia um thread chamando o método run.
join([time]) - O join () aguarda o término dos threads.
isAlive() - O método isAlive () verifica se uma thread ainda está em execução.
getName() - O método getName () retorna o nome de um thread.
setName() - O método setName () define o nome de um thread.
Criação de rosca usando módulo de rosqueamento
Para implementar um novo thread usando o módulo de threading, você deve fazer o seguinte -
Defina uma nova subclasse da classe Thread .
Substitua o método __init __ (self [, args]) para adicionar argumentos adicionais.
Em seguida, substitua o método run (self [, args]) para implementar o que o thread deve fazer quando iniciado.
Depois de criar a nova subclasse Thread , você pode criar uma instância dela e então iniciar uma nova thread invocando start () , que por sua vez chama o método run () .
Exemplo
#!/usr/bin/python
import threading
import time
exitFlag = 0
class myThread (threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print "Starting " + self.name
print_time(self.name, 5, self.counter)
print "Exiting " + self.name
def print_time(threadName, counter, delay):
while counter:
if exitFlag:
threadName.exit()
time.sleep(delay)
print "%s: %s" % (threadName, time.ctime(time.time()))
counter -= 1
# Create new threads
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# Start new Threads
thread1.start()
thread2.start()
print "Exiting Main Thread"
Quando o código acima é executado, ele produz o seguinte resultado -
Starting Thread-1
Starting Thread-2
Exiting Main Thread
Thread-1: Thu Mar 21 09:10:03 2013
Thread-1: Thu Mar 21 09:10:04 2013
Thread-2: Thu Mar 21 09:10:04 2013
Thread-1: Thu Mar 21 09:10:05 2013
Thread-1: Thu Mar 21 09:10:06 2013
Thread-2: Thu Mar 21 09:10:06 2013
Thread-1: Thu Mar 21 09:10:07 2013
Exiting Thread-1
Thread-2: Thu Mar 21 09:10:08 2013
Thread-2: Thu Mar 21 09:10:10 2013
Thread-2: Thu Mar 21 09:10:12 2013
Exiting Thread-2
Sincronizando Threads
O módulo de threading fornecido com Python inclui um mecanismo de bloqueio simples de implementar que permite sincronizar threads. Um novo bloqueio é criado chamando o método Lock () , que retorna o novo bloqueio.
O método de aquisição (bloqueio) do novo objeto de bloqueio é usado para forçar a execução de threads de forma síncrona. O parâmetro opcional de bloqueio permite controlar se o encadeamento espera para adquirir o bloqueio.
Se o bloqueio for definido como 0, o thread retorna imediatamente com um valor 0 se o bloqueio não pode ser adquirido e com 1 se o bloqueio foi adquirido. Se o bloqueio for definido como 1, o thread é bloqueado e espera que o bloqueio seja liberado.
O método release () do novo objeto de bloqueio é usado para liberar o bloqueio quando não for mais necessário.
Exemplo
#!/usr/bin/python
import threading
import time
class myThread (threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print "Starting " + self.name
# Get lock to synchronize threads
threadLock.acquire()
print_time(self.name, self.counter, 3)
# Free lock to release next thread
threadLock.release()
def print_time(threadName, delay, counter):
while counter:
time.sleep(delay)
print "%s: %s" % (threadName, time.ctime(time.time()))
counter -= 1
threadLock = threading.Lock()
threads = []
# Create new threads
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# Start new Threads
thread1.start()
thread2.start()
# Add threads to thread list
threads.append(thread1)
threads.append(thread2)
# Wait for all threads to complete
for t in threads:
t.join()
print "Exiting Main Thread"
Quando o código acima é executado, ele produz o seguinte resultado -
Starting Thread-1
Starting Thread-2
Thread-1: Thu Mar 21 09:11:28 2013
Thread-1: Thu Mar 21 09:11:29 2013
Thread-1: Thu Mar 21 09:11:30 2013
Thread-2: Thu Mar 21 09:11:32 2013
Thread-2: Thu Mar 21 09:11:34 2013
Thread-2: Thu Mar 21 09:11:36 2013
Exiting Main Thread
Fila de prioridade multithread
O módulo Queue permite criar um novo objeto de fila que pode conter um número específico de itens. Existem métodos a seguir para controlar a fila -
get() - O get () remove e retorna um item da fila.
put() - A colocação adiciona o item a uma fila.
qsize() - O qsize () retorna o número de itens que estão atualmente na fila.
empty()- O empty () retorna True se a fila está vazia; caso contrário, False.
full()- o full () retorna True se a fila estiver cheia; caso contrário, False.
Exemplo
#!/usr/bin/python
import Queue
import threading
import time
exitFlag = 0
class myThread (threading.Thread):
def __init__(self, threadID, name, q):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.q = q
def run(self):
print "Starting " + self.name
process_data(self.name, self.q)
print "Exiting " + self.name
def process_data(threadName, q):
while not exitFlag:
queueLock.acquire()
if not workQueue.empty():
data = q.get()
queueLock.release()
print "%s processing %s" % (threadName, data)
else:
queueLock.release()
time.sleep(1)
threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = Queue.Queue(10)
threads = []
threadID = 1
# Create new threads
for tName in threadList:
thread = myThread(threadID, tName, workQueue)
thread.start()
threads.append(thread)
threadID += 1
# Fill the queue
queueLock.acquire()
for word in nameList:
workQueue.put(word)
queueLock.release()
# Wait for queue to empty
while not workQueue.empty():
pass
# Notify threads it's time to exit
exitFlag = 1
# Wait for all threads to complete
for t in threads:
t.join()
print "Exiting Main Thread"
Quando o código acima é executado, ele produz o seguinte resultado -
Starting Thread-1
Starting Thread-2
Starting Thread-3
Thread-1 processing One
Thread-2 processing Two
Thread-3 processing Three
Thread-1 processing Four
Thread-2 processing Five
Exiting Thread-3
Exiting Thread-1
Exiting Thread-2
Exiting Main Thread