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 threading
módulo:
import threading
Linguagem de código: Python ( python )
Segundo, crie um Semaphore
objeto 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 Semaphore
objeto 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 with
instrução:
with semaphore:
# Code within this block has acquired the semaphore
# Perform operations on the shared resource
# ...
# The semaphore is released outside the with block
Linguagem de código: Python ( python )
A with
instruçã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.txt
Linguagem 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.request
os módulos:
import threading
import urllib.request
Linguagem 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.request
mó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 data
Linguagem 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.