Semáforo Python

Resumo : neste tutorial, você aprenderá como usar o Python Semaphore para controlar o número de threads que podem acessar um recurso compartilhado simultaneamente.

Introdução ao semáforo Python

Um semáforo Python é uma primitiva de sincronização que permite controlar o acesso a um recurso compartilhado. Basicamente, um semáforo é um contador associado a um bloqueio que limita o número de threads que podem acessar um recurso compartilhado simultaneamente.

Um semáforo ajuda a evitar problemas de sincronização de threads, como condições de corrida, em que vários threads tentam acessar o recurso ao mesmo tempo e interferem nas operações uns dos outros.

Um semáforo mantém uma contagem. Quando um thread deseja acessar o recurso compartilhado, o semáforo verifica a contagem.

Se a contagem for maior que zero, a contagem diminui e permite que o thread acesse o recurso. Se a contagem for zero, o semáforo bloqueia o thread até que a contagem seja maior que zero.

Um semáforo tem duas operações principais:

  • Adquirir: a operação de aquisição verifica a contagem e a decrementa se for maior que zero. Se a contagem for zero, o semáforo bloqueará o thread até que outro thread libere o semáforo.
  • Liberação: a operação de liberação incrementa as contagens que permitem que outros threads a adquiram.

Usando um semáforo Python

Para usar o semáforo, siga estas etapas:

Primeiro, importe o threadingmódulo:

import threadingLinguagem de código:  Python  ( python )

Segundo, crie um Semaphoreobjeto e especifique o número de threads que podem adquiri-lo ao mesmo tempo:

semaphore = threading.Semaphore(3)Linguagem de código:  Python  ( python )

Neste exemplo, criamos um Semaphoreobjeto que permite que apenas três threads o adquiram ao mesmo tempo.

Terceiro, adquira um semáforo de um thread chamando o acquire()método:

semaphore.acquire()Linguagem de código:  Python  ( python )

Se a contagem do semáforo for zero, o thread aguardará até que outro thread libere o semáforo. Depois de ter o semáforo, você pode executar uma seção crítica do código.

Finalmente, libere um semáforo após executar a seção crítica do código chamando o release()método:

semaphore.release()Linguagem de código:  Python  ( python )

Para garantir que um semáforo seja adquirido e liberado corretamente, mesmo que ocorram exceções durante a execução da seção crítica de um código, você pode usar a withinstrução:

with semaphore:
    # Code within this block has acquired the semaphore

    # Perform operations on the shared resource
    # ...
    
# The semaphore is released outside the with blockLinguagem de código:  Python  ( python )

A withinstrução adquire e libera o semáforo automaticamente, tornando seu código menos sujeito a erros.

Exemplo de semáforo Python

O exemplo a seguir ilustra como usar o semáforo para limitar o número máximo de downloads simultâneos a três usando multithreading em Python:

import threading
import urllib.request

MAX_CONCURRENT_DOWNLOADS = 3
semaphore = threading.Semaphore(MAX_CONCURRENT_DOWNLOADS)

def download(url):
    with semaphore:
        print(f"Downloading {url}...")
        
        response = urllib.request.urlopen(url)
        data = response.read()
        
        print(f"Finished downloading {url}")

        return data

        

def main():
    # URLs to download
    urls = [
        'https://www.ietf.org/rfc/rfc791.txt',
        'https://www.ietf.org/rfc/rfc792.txt',
        'https://www.ietf.org/rfc/rfc793.txt',
        'https://www.ietf.org/rfc/rfc794.txt',
        'https://www.ietf.org/rfc/rfc795.txt',
    ]

    # Create threads for each download
    threads = []
    for url in urls:
        thread = threading.Thread(target=download, args=(url,))
        threads.append(thread)
        thread.start()

    # Wait for all threads to complete
    for thread in threads:
        thread.join()


if __name__ == '__main__':
    main()Linguagem de código:  Python  ( python )

Saída:

Downloading https://www.ietf.org/rfc/rfc791.txt...
Downloading https://www.ietf.org/rfc/rfc792.txt...
Downloading https://www.ietf.org/rfc/rfc793.txt...
Finished downloading https://www.ietf.org/rfc/rfc792.txt
Downloading https://www.ietf.org/rfc/rfc794.txt...
Finished downloading https://www.ietf.org/rfc/rfc791.txt
Downloading https://www.ietf.org/rfc/rfc795.txt...
Finished downloading https://www.ietf.org/rfc/rfc793.txt
Finished downloading https://www.ietf.org/rfc/rfc794.txt
Finished downloading https://www.ietf.org/rfc/rfc795.txtLinguagem de código:  Python  ( python )

A saída mostra que apenas no máximo três threads podem ser baixados ao mesmo tempo:

Downloading https://www.ietf.org/rfc/rfc791.txt...
Downloading https://www.ietf.org/rfc/rfc792.txt...
Downloading https://www.ietf.org/rfc/rfc793.txt...Linguagem de código:  Python  ( python )

Quando o número de threads chegar a três, o próximo thread precisará aguardar que o semáforo seja liberado por outro thread.

Por exemplo, o exemplo a seguir mostra que o thread nº 2 concluiu e liberou o semáforo, e o próximo thread começa a baixar o URLhttps://www.ietf.org/rfc/rfc794.txt

Finished downloading https://www.ietf.org/rfc/rfc792.txt
Downloading https://www.ietf.org/rfc/rfc794.txt...Linguagem de código:  Python  ( python )

Como funciona o programa.

Primeiro, importe o threading e urlib.requestos módulos:

import threading
import urllib.requestLinguagem de código:  Python  ( python )

Em segundo lugar, crie um objeto Semaphore para controlar o número de threads que podem ser baixados simultaneamente para três:

MAX_CONCURRENT_DOWNLOADS = 3
semaphore = threading.Semaphore(MAX_CONCURRENT_DOWNLOADS)Linguagem de código:  Python  ( python )

Terceiro, defina a download()função que faz download de um URL. A função de download adquire e libera o semáforo usando a instrução with. Ele também usa o urllib.requestmódulo para baixar dados de uma URL:

def download(url):
    with semaphore:
        print(f"Downloading {url}...")
        
        response = urllib.request.urlopen(url)
        data = response.read()
        
        print(f"Finished downloading {url}")

        return dataLinguagem de código:  Python  ( python )

Quarto, defina a main()função que cria cinco threads com base em uma lista de URLs e os inicia para baixar dados:

def main():
    # URLs to download
    urls = [
        'https://www.ietf.org/rfc/rfc791.txt',
        'https://www.ietf.org/rfc/rfc792.txt',
        'https://www.ietf.org/rfc/rfc793.txt',
        'https://www.ietf.org/rfc/rfc794.txt',
        'https://www.ietf.org/rfc/rfc795.txt',
    ]

    # Create threads for each download
    threads = []
    for url in urls:
        thread = threading.Thread(target=download, args=(url,))
        threads.append(thread)
        thread.start()

    # Wait for all threads to complete
    for thread in threads:
        thread.join()Linguagem de código:  Python  ( python )

Por fim, chame a main()função na seção if __name__ == ‘__main__’:

if __name__ == '__main__':
    main()Linguagem de código:  Python  ( python )

Resumo

  • Use o semáforo Python para controlar o número de threads que podem acessar um recurso compartilhado simultaneamente.

Deixe um comentário

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