Implementação de Threads

Neste capítulo, aprenderemos como implementar threads em Python.

Módulo Python para implementação de thread

Os threads do Python às vezes são chamados de processos leves porque os threads ocupam muito menos memória do que os processos. Threads permitem realizar várias tarefas ao mesmo tempo. Em Python, temos os dois módulos a seguir que implementam threads em um programa -

  • <_thread>module

  • <threading>module

A principal diferença entre esses dois módulos é que <_thread> módulo trata um thread como uma função, enquanto o <threading>O módulo trata cada thread como um objeto e o implementa de uma forma orientada a objetos. Além disso, o<_thread>módulo é eficaz em threading de baixo nível e tem menos recursos do que o <threading> módulo.

módulo <_thread>

Na versão anterior do Python, tínhamos o <thread>módulo, mas tem sido considerado como "obsoleto" por um longo tempo. Os usuários foram incentivados a usar o<threading>módulo em vez disso. Portanto, no Python 3 o módulo "thread" não está mais disponível. Foi renomeado para "<_thread>"para incompatibilidades com versões anteriores em Python3.

Para gerar um novo tópico com a ajuda do <_thread> módulo, precisamos chamar o start_new_threadmétodo disso. O funcionamento deste método pode ser compreendido com a ajuda da seguinte sintaxe -

_thread.start_new_thread ( function, args[, kwargs] )

Aqui -

  • args é uma tupla de argumentos

  • kwargs é um dicionário opcional de argumentos de palavras-chave

Se quisermos chamar a função sem passar um argumento, então precisamos usar uma tupla vazia de argumentos em args.

Essa chamada de método retorna imediatamente, o thread filho é iniciado e chama a função com a lista passada, se houver, de args. O encadeamento termina como e quando a função retorna.

Exemplo

A seguir está um exemplo para gerar um novo encadeamento usando o <_thread>módulo. Estamos usando o método start_new_thread () aqui.

import _thread
import time

def print_time( threadName, delay):
   count = 0
   while count < 5:
      time.sleep(delay)
      count += 1
      print ("%s: %s" % ( threadName, time.ctime(time.time()) ))

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

A saída a seguir nos ajudará a entender a geração de novos threads com a ajuda do <_thread> módulo.

Thread-1: Mon Apr 23 10:03:33 2018
Thread-2: Mon Apr 23 10:03:35 2018
Thread-1: Mon Apr 23 10:03:35 2018
Thread-1: Mon Apr 23 10:03:37 2018
Thread-2: Mon Apr 23 10:03:39 2018
Thread-1: Mon Apr 23 10:03:39 2018
Thread-1: Mon Apr 23 10:03:41 2018
Thread-2: Mon Apr 23 10:03:43 2018
Thread-2: Mon Apr 23 10:03:47 2018
Thread-2: Mon Apr 23 10:03:51 2018

Módulo <threading>

o <threading>O módulo implementa de forma orientada a objetos e trata cada thread como um objeto. Portanto, ele fornece um suporte de alto nível muito mais poderoso para threads do que o módulo <_thread>. Este módulo está incluído no Python 2.4.

Métodos adicionais no módulo <threading>

o <threading> módulo compreende todos os métodos do <_thread>módulo, mas também fornece métodos adicionais. Os métodos adicionais são os seguintes -

  • threading.activeCount() - Este método retorna o número de objetos de thread que estão ativos

  • threading.currentThread() - Este método retorna o número de objetos de thread no controle de thread do chamador.

  • threading.enumerate() - Este método retorna uma lista de todos os objetos de thread que estão atualmente ativos.

  • Para implementar o threading, o <threading> módulo tem o Thread classe que fornece os seguintes métodos -

    • 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.

Como criar threads usando o módulo <threading>?

Nesta seção, aprenderemos como criar threads usando o <threading>módulo. Siga estas etapas para criar um novo tópico usando o módulo <threading> -

  • Step 1 - Nesta etapa, precisamos definir uma nova subclasse do Thread classe.

  • Step 2 - Então, para adicionar argumentos adicionais, precisamos substituir o __init__(self [,args]) método.

  • Step 3 - Nesta etapa, precisamos substituir o método run (self [, args]) para implementar o que o thread deve fazer quando iniciado.

  • Agora, depois de criar o novo Thread subclasse, podemos criar uma instância dela e, em seguida, iniciar uma nova thread invocando o start(), que por sua vez chama o run() método.

Exemplo

Considere este exemplo para aprender como gerar um novo thread usando o <threading> módulo.

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

thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

thread1.start()
thread2.start()
thread1.join()
thread2.join()
print ("Exiting Main Thread")
Starting Thread-1
Starting Thread-2

Resultado

Agora, considere a seguinte saída -

Thread-1: Mon Apr 23 10:52:09 2018
Thread-1: Mon Apr 23 10:52:10 2018
Thread-2: Mon Apr 23 10:52:10 2018
Thread-1: Mon Apr 23 10:52:11 2018
Thread-1: Mon Apr 23 10:52:12 2018
Thread-2: Mon Apr 23 10:52:12 2018
Thread-1: Mon Apr 23 10:52:13 2018
Exiting Thread-1
Thread-2: Mon Apr 23 10:52:14 2018
Thread-2: Mon Apr 23 10:52:16 2018
Thread-2: Mon Apr 23 10:52:18 2018
Exiting Thread-2
Exiting Main Thread

Programa Python para vários estados de thread

Existem cinco estados de thread - novo, executável, em execução, em espera e morto. Entre esses cinco Desses cinco, vamos nos concentrar principalmente em três estados - funcionando, esperando e morto. Um thread obtém seus recursos no estado de execução, espera pelos recursos no estado de espera; a liberação final do recurso, se em execução e adquirido, está no estado morto.

O programa Python a seguir, com a ajuda dos métodos start (), sleep () e join (), mostrará como uma thread entrou em execução, espera e estado morto, respectivamente.

Step 1 - Importe os módulos necessários, <threading> e <time>

import threading
import time

Step 2 - Defina uma função, que será chamada durante a criação de um segmento.

def thread_states():
   print("Thread entered in running state")

Step 3 - Estamos usando o método sleep () do módulo time para fazer nosso thread esperar, digamos, 2 segundos.

time.sleep(2)

Step 4 - Agora, estamos criando uma thread chamada T1, que leva o argumento da função definida acima.

T1 = threading.Thread(target=thread_states)

Step 5- Agora, com a ajuda da função start (), podemos iniciar nosso thread. Ele produzirá a mensagem, que foi definida por nós durante a definição da função.

T1.start()
Thread entered in running state

Step 6 - Agora, finalmente podemos matar a thread com o método join () após terminar sua execução.

T1.join()

Iniciando um thread em Python

Em python, podemos iniciar um novo thread de maneiras diferentes, mas a mais fácil entre elas é defini-lo como uma única função. Depois de definir a função, podemos passar isso como o destino para um novothreading.Threadobjeto e assim por diante. Execute o seguinte código Python para entender como a função funciona -

import threading
import time
import random
def Thread_execution(i):
   print("Execution of Thread {} started\n".format(i))
   sleepTime = random.randint(1,4)
   time.sleep(sleepTime)
   print("Execution of Thread {} finished".format(i))
for i in range(4):
   thread = threading.Thread(target=Thread_execution, args=(i,))
   thread.start()
   print("Active Threads:" , threading.enumerate())

Resultado

Execution of Thread 0 started
Active Threads:
   [<_MainThread(MainThread, started 6040)>,
      <HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
      <Thread(Thread-3576, started 3932)>]

Execution of Thread 1 started
Active Threads:
   [<_MainThread(MainThread, started 6040)>,
      <HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
      <Thread(Thread-3576, started 3932)>,
      <Thread(Thread-3577, started 3080)>]

Execution of Thread 2 started
Active Threads:
   [<_MainThread(MainThread, started 6040)>,
      <HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
      <Thread(Thread-3576, started 3932)>,
      <Thread(Thread-3577, started 3080)>,
      <Thread(Thread-3578, started 2268)>]

Execution of Thread 3 started
Active Threads:
   [<_MainThread(MainThread, started 6040)>,
      <HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
      <Thread(Thread-3576, started 3932)>,
      <Thread(Thread-3577, started 3080)>,
      <Thread(Thread-3578, started 2268)>,
      <Thread(Thread-3579, started 4520)>]
Execution of Thread 0 finished
Execution of Thread 1 finished
Execution of Thread 2 finished
Execution of Thread 3 finished

Threads daemon em Python

Antes de implementar as threads daemon em Python, precisamos saber sobre as threads daemon e seu uso. Em termos de computação, o daemon é um processo em segundo plano que lida com as solicitações de vários serviços, como envio de dados, transferência de arquivos, etc. Ele ficaria inativo se não fosse mais necessário. A mesma tarefa também pode ser realizada com a ajuda de threads que não sejam daemon. No entanto, neste caso, a thread principal deve manter o controle das threads que não são daemon manualmente. Por outro lado, se estivermos usando encadeamentos daemon, o encadeamento principal pode esquecer completamente isso e será encerrado quando o encadeamento principal for encerrado. Outro ponto importante sobre os encadeamentos daemon é que podemos optar por usá-los apenas para tarefas não essenciais que não nos afetariam se não fossem concluídos ou fossem eliminados no meio. A seguir está a implementação de threads daemon em python -

import threading
import time

def nondaemonThread():
   print("starting my thread")
   time.sleep(8)
   print("ending my thread")
def daemonThread():
   while True:
   print("Hello")
   time.sleep(2)
if __name__ == '__main__':
   nondaemonThread = threading.Thread(target = nondaemonThread)
   daemonThread = threading.Thread(target = daemonThread)
   daemonThread.setDaemon(True)
   daemonThread.start()
   nondaemonThread.start()

No código acima, existem duas funções, a saber >nondaemonThread() e >daemonThread(). A primeira função imprime seu estado e dorme após 8 segundos, enquanto a função deamonThread () imprime Hello a cada 2 segundos indefinidamente. Podemos entender a diferença entre threads não demoníacos e daemon com a ajuda da seguinte saída -

Hello

starting my thread
Hello
Hello
Hello
Hello
ending my thread
Hello
Hello
Hello
Hello
Hello