Threading Python – 03

Resumo : neste tutorial, você aprenderá como usar o módulo de threading Python para desenvolver aplicativos multithread.

Aplicativos de thread único

Vamos começar com um programa simples:

from time import sleep, perf_counter

def task():
    print('Starting a task...')
    sleep(1)
    print('done')


start_time = perf_counter()

task()
task()

end_time = perf_counter()

print(f'It took {end_time- start_time: 0.2f} second(s) to complete.')
Linguagem de código:  Python  ( python )

Como funciona.

Primeiro, importe as funções sleep()e perf_counter()do timemódulo:

from time import sleep, perf_counterLinguagem de código:  Python  ( python )

Segundo, defina uma função que leva um segundo para ser concluída:

def task():
    print('Starting a task...')
    sleep(1)
    print('done')Linguagem de código:  Python  ( python )

Terceiro, obtenha o valor do contador de desempenho chamando a perf_counter()função:

start_time = perf_counter()       Linguagem de código:  Python  ( python )

Quarto, chame a task()função duas vezes:

task()
task()Linguagem de código:  Python  ( python )

Quinto, obtenha o valor do contador de desempenho chamando a perf_counter()função:

end_time = perf_counter()Linguagem de código:  Python  ( python )

Por fim, imprima o tempo que leva para concluir a execução da task()função duas vezes:

print(f'It took {end_time- start_time: 0.2f} second(s) to complete.')Linguagem de código:  Python  ( python )

Aqui está o resultado:

Starting a task...
done
Starting a task...
done
It took  2.00 second(s) to complete.Linguagem de código:  Python  ( python )

Como você pode esperar, o programa leva cerca de dois segundos para ser concluído. Se você chamar a task()função 10 vezes, levará cerca de 10 segundos para ser concluída.

O diagrama a seguir ilustra como o programa funciona:

Primeiro, a task()função é executada e dorme por um segundo. Em seguida, ele é executado pela segunda vez e também dorme por mais um segundo. Finalmente, o programa é concluído.

Quando a task()função chama a sleep()função, a CPU fica ociosa. Em outras palavras, a CPU não faz nada, o que não é eficiente em termos de utilização de recursos.

Este programa possui um processo com um único thread, que é chamado de thread principal . Como o programa possui apenas um thread, ele é chamado de programa de thread único.

Usando threading Python para desenvolver um exemplo de programa multithread

Para criar um programa multithread, você precisa usar o threadingmódulo Python.

Primeiro, importe a Threadclasse do threadingmódulo:

from threading import ThreadLinguagem de código:  Python  ( python )

Segundo, crie um novo thread instanciando uma instância da Threadclasse:

new_thread = Thread(target=fn,args=args_tuple)Linguagem de código:  Python  ( python )

O Thread()aceita muitos parâmetros. Os principais são:

  • target: especifica uma função ( fn) para ser executada no novo thread.
  • args: especifica os argumentos da função ( fn). O argsargumento é uma tupla.

Terceiro, inicie o thread chamando o start()método da Threadinstância:

new_thread.start()Linguagem de código:  Python  ( python )

Se quiser esperar a conclusão do thread no thread principal, você pode chamar o join()método:

new_thread.join()Linguagem de código:  Python  ( python )

Ao chamar o join()método, o thread principal aguardará a conclusão do thread filho antes de ser encerrado.

O programa a seguir ilustra como usar o threadingmódulo:

from time import sleep, perf_counter
from threading import Thread


def task():
    print('Starting a task...')
    sleep(1)
    print('done')


start_time = perf_counter()

# create two new threads
t1 = Thread(target=task)
t2 = Thread(target=task)

# start the threads
t1.start()
t2.start()

# wait for the threads to complete
t1.join()
t2.join()

end_time = perf_counter()

print(f'It took {end_time- start_time: 0.2f} second(s) to complete.')Linguagem de código:  Python  ( python )

Como funciona. (e vamos nos concentrar apenas na parte de rosqueamento)

Primeiro, crie dois novos tópicos:

t1 = Thread(target=task) 
t2 = Thread(target=task)

Segundo, inicie ambos os threads chamando o start()método:

t1.start()
t2.start()Linguagem de código:  Python  ( python )

Terceiro, aguarde a conclusão de ambos os threads:

t1.join()
t2.join()Linguagem de código:  Python  ( python )

Finalmente, mostre o tempo de execução:

print(f'It took {end_time- start_time: 0.2f} second(s) to complete.')Linguagem de código:  Python  ( python )

Saída:

Starting a task...
Starting a task...
done
done
It took  1.00 second(s) to complete.Linguagem de código:  Python  ( python )

Quando o programa for executado, ele terá três threads: o thread principal e dois outros threads filhos.

Conforme mostrado claramente na saída, o programa levou um segundo em vez de dois para ser concluído.

O diagrama a seguir mostra como os threads são executados:

Passando argumentos para threads

O programa a seguir mostra como passar argumentos para a função atribuída a um thread:

from time import sleep, perf_counter
from threading import Thread


def task(id):
    print(f'Starting the task {id}...')
    sleep(1)
    print(f'The task {id} completed')


start_time = perf_counter()

# create and start 10 threads
threads = []
for n in range(1, 11):
    t = Thread(target=task, args=(n,))
    threads.append(t)
    t.start()

# wait for the threads to complete
for t in threads:
    t.join()

end_time = perf_counter()

print(f'It took {end_time- start_time: 0.2f} second(s) to complete.')Linguagem de código:  Python  ( python )

Como funciona.

Primeiro, defina uma task()função que aceite um argumento:

def task(id):
    print(f'Starting the task {id}...')
    sleep(1)
    print(f'The task {id} completed')Linguagem de código:  Python  ( python )

Segundo, crie 10 novos threads e passe um ID para cada um. A threadslista é usada para acompanhar todos os tópicos recém-criados:

threads = []
for n in range(1, 11):
    t = Thread(target=task, args=(n,))
    threads.append(t)
    t.start()Linguagem de código:  Python  ( python )

Observe que se você chamar o join()método dentro do loop, o programa aguardará a conclusão do primeiro thread antes de iniciar o próximo.

Terceiro, espere que todos os threads sejam concluídos chamando o join()método:

for t in threads:
    t.join()Linguagem de código:  Python  ( python )

O seguinte mostra a saída do programa:

Starting the task 1...
Starting the task 2...
Starting the task 3...
Starting the task 4...
Starting the task 5...
Starting the task 6...
Starting the task 7...
Starting the task 8...
Starting the task 9...
Starting the task 10...
The task 10 completed
The task 8 completed
The task 1 completed
The task 6 completed
The task 7 completed
The task 9 completed
The task 3 completed
The task 4 completed
The task 2 completed
The task 5 completed
It took  1.02 second(s) to complete.Linguagem de código:  Python  ( python )

Demorou apenas 1,05 segundos para ser concluído.

Observe que o programa não executa o thread na ordem de 1 a 10.

Quando usar threading Python

Conforme apresentado no tutorial de processo e thread , existem dois tipos principais de tarefas:

  • Tarefas vinculadas a E/S – o tempo gasto em E/S é significativamente maior do que o tempo gasto em computação.
  • Tarefas vinculadas à CPU – o tempo gasto em computação é significativamente maior do que o tempo de espera por E/S.

O threading Python é otimizado para tarefas vinculadas a E/S. Por exemplo, solicitar recursos remotos, conectar um servidor de banco de dados ou ler e gravar arquivos.

Um exemplo prático de threading em Python

Suponha que você tenha uma lista de arquivos de texto em uma pasta, por exemplo, C:/temp/. E você deseja substituir um texto por um novo em todos os arquivos.

O seguinte programa de thread único mostra como substituir uma substring pela nova nos arquivos de texto:

from time import perf_counter


def replace(filename, substr, new_substr):
    print(f'Processing the file {filename}')
    # get the contents of the file
    with open(filename, 'r') as f:
        content = f.read()

    # replace the substr by new_substr
    content = content.replace(substr, new_substr)

    # write data into the file
    with open(filename, 'w') as f:
        f.write(content)


def main():
    filenames = [
        'c:/temp/test1.txt',
        'c:/temp/test2.txt',
        'c:/temp/test3.txt',
        'c:/temp/test4.txt',
        'c:/temp/test5.txt',
        'c:/temp/test6.txt',
        'c:/temp/test7.txt',
        'c:/temp/test8.txt',
        'c:/temp/test9.txt',
        'c:/temp/test10.txt',
    ]

    for filename in filenames:
        replace(filename, 'ids', 'id')


if __name__ == "__main__":
    start_time = perf_counter()

    main()

    end_time = perf_counter()
    print(f'It took {end_time- start_time :0.2f} second(s) to complete.')
Linguagem de código:  Python  ( python )

Saída:

It took 0.16 second(s) to complete.Linguagem de código:  Python  ( python )

O programa a seguir tem a mesma funcionalidade. No entanto, ele usa vários threads:

from threading import Thread
from time import perf_counter


def replace(filename, substr, new_substr):
    print(f'Processing the file {filename}')
    # get the contents of the file
    with open(filename, 'r') as f:
        content = f.read()

    # replace the substr by new_substr
    content = content.replace(substr, new_substr)

    # write data into the file
    with open(filename, 'w') as f:
        f.write(content)


def main():
    filenames = [
        'c:/temp/test1.txt',
        'c:/temp/test2.txt',
        'c:/temp/test3.txt',
        'c:/temp/test4.txt',
        'c:/temp/test5.txt',
        'c:/temp/test6.txt',
        'c:/temp/test7.txt',
        'c:/temp/test8.txt',
        'c:/temp/test9.txt',
        'c:/temp/test10.txt',
    ]

    # create threads
    threads = [Thread(target=replace, args=(filename, 'id', 'ids'))
            for filename in filenames]

    # start the threads
    for thread in threads:
        thread.start()

    # wait for the threads to complete
    for thread in threads:
        thread.join()


if __name__ == "__main__":
    start_time = perf_counter()

    main()

    end_time = perf_counter()
    print(f'It took {end_time- start_time :0.2f} second(s) to complete.')
Linguagem de código:  Python  ( python )

Saída:

Processing the file c:/temp/test1.txt
Processing the file c:/temp/test2.txt
Processing the file c:/temp/test3.txt
Processing the file c:/temp/test4.txt
Processing the file c:/temp/test5.txt
Processing the file c:/temp/test6.txt
Processing the file c:/temp/test7.txt
Processing the file c:/temp/test8.txt
Processing the file c:/temp/test9.txt
Processing the file c:/temp/test10.txt
It took 0.02 second(s) to complete.Linguagem de código:  Python  ( python )

Como você pode ver claramente na saída, o programa multithread é executado muito mais rápido.

Resumo

  • Use o módulo Python threadingpara criar um aplicativo multithread.
  • Use o Thread(function, args)para criar um novo tópico.
  • Chame o start()método da Threadclasse para iniciar o thread.
  • Chame o join()método da Threadclasse para aguardar a conclusão do thread no thread principal.
  • Use threading apenas para aplicativos de processamento vinculados a E/S.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *