Python 3 - 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 são 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.
Existem dois tipos diferentes de tópicos -
- fio do kernel
- discussão do usuário
Os threads do kernel fazem parte do sistema operacional, enquanto os threads do espaço do usuário não são implementados no kernel.
Existem dois módulos que suportam o uso de threads em Python3 -
- _thread
- threading
O módulo thread está "obsoleto" há um bom tempo. Os usuários são encorajados a usar o módulo de threading. Portanto, no Python 3, o módulo "thread" não está mais disponível. No entanto, ele foi renomeado para "_thread" para compatibilidade com versões anteriores no Python3.
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/python3
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
Resultado
Quando o código acima é executado, ele produz o seguinte resultado -
Thread-1: Fri Feb 19 09:41:39 2016
Thread-2: Fri Feb 19 09:41:41 2016
Thread-1: Fri Feb 19 09:41:41 2016
Thread-1: Fri Feb 19 09:41:43 2016
Thread-2: Fri Feb 19 09:41:45 2016
Thread-1: Fri Feb 19 09:41:45 2016
Thread-1: Fri Feb 19 09:41:47 2016
Thread-2: Fri Feb 19 09:41:49 2016
Thread-2: Fri Feb 19 09:41:53 2016
O programa segue em um loop infinito. Você terá que pressionar ctrl-c para parar
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/python3
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, self.counter, 5)
print ("Exiting " + self.name)
def print_time(threadName, delay, counter):
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()
thread1.join()
thread2.join()
print ("Exiting Main Thread")
Resultado
Quando executamos o programa acima, ele produz o seguinte resultado -
Starting Thread-1
Starting Thread-2
Thread-1: Fri Feb 19 10:00:21 2016
Thread-2: Fri Feb 19 10:00:22 2016
Thread-1: Fri Feb 19 10:00:22 2016
Thread-1: Fri Feb 19 10:00:23 2016
Thread-2: Fri Feb 19 10:00:24 2016
Thread-1: Fri Feb 19 10:00:24 2016
Thread-1: Fri Feb 19 10:00:25 2016
Exiting Thread-1
Thread-2: Fri Feb 19 10:00:26 2016
Thread-2: Fri Feb 19 10:00:28 2016
Thread-2: Fri Feb 19 10:00:30 2016
Exiting Thread-2
Exiting Main Thread
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 dos 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/python3
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")
Resultado
Quando o código acima é executado, ele produz o seguinte resultado -
Starting Thread-1
Starting Thread-2
Thread-1: Fri Feb 19 10:04:14 2016
Thread-1: Fri Feb 19 10:04:15 2016
Thread-1: Fri Feb 19 10:04:16 2016
Thread-2: Fri Feb 19 10:04:18 2016
Thread-2: Fri Feb 19 10:04:20 2016
Thread-2: Fri Feb 19 10:04:22 2016
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/python3
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")
Resultado
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