Mudanças entre as edições de "Python Concorrente"
(15 revisões intermediárias pelo mesmo usuário não estão sendo mostradas) | |||
Linha 1: | Linha 1: | ||
+ | [[Arquivo:Muitos-pratos-girando.jpg|thumb|320px|O malabarista de pratos é um só, mas trabalhando de modo concorrente ele mantém 18 pratos girando ao mesmo tempo, dedicando um pouco de tempo a cada prato.]] |
||
+ | |||
+ | = Prática = |
||
+ | |||
⚫ | |||
+ | |||
+ | |||
+ | = Teoria = |
||
+ | == Concorrência x paralelismo == |
||
+ | |||
<blockquote> |
<blockquote> |
||
Concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once.<br> |
Concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once.<br> |
||
Linha 5: | Linha 15: | ||
+ | Rob Pike (citado acima) define assim em termos bem simples: "Concorrência é lidar com várias coisas ao mesmo tempo. Paralelismo é fazer várias coisas ao mesmo tempo." O importante na concorrência é manter várias atividades simultâneas ativas, mesmo que só uma avance de cada vez. |
||
⚫ | |||
+ | === Exemplo culinário === |
||
− | Ao contrário de Go e Erlang, Python não foi originalmente projetada com foco em programação concorrente. O interpretador padrão (CPython) tem uma trava global (a GIL) que impede a execução simultânea de threads escritas em Python. Isso não era um problema quando as CPUs tinham apenas um núcleo, mas hoje até celulares tem CPUs multi-core. Esta é uma limitação que existe também no interpretador padrão de Ruby (MRI), e o motivo de existir é simplificar o código em C do interpretador e facilitar a intergração de bibliotecas externas em C que não são thread-safe. As implementações de Python e Ruby em Java (Jython e JRuby) não tem a limitação da GIL. |
||
+ | Ao preparar vários pratos ao mesmo tempo o cozinheiro está trabalhando de forma concorrente, porque normalmente ele só cuida de um prato por vez, mas vários estão sendo preparados ao mesmo tempo. Por outro lado, um fogão de 6 bocas permite aquecer 6 panelas em paralelo. Ou seja, o cozinheiro trabalha em modo concorrente e o fogão em modo paralelo. |
||
+ | |||
+ | |||
+ | == Sobre Concorrência em Python == |
||
+ | |||
+ | Ao contrário de Go, Python não foi originalmente projetada com foco em programação concorrente, muito menos paralela. O modo tradicional de programar concorrência em Python -- threads -- é limitado no interpretador padrão (CPython) por uma trava global (a GIL) que impede a execução paralela de threads escritas em Python. Isso significa que threads em Python são úteis apenas em aplicações ''I/O bound'' -- onde o gargalo está no I/O, como é o caso de aplicações na Internet. |
||
=== Usando threads apesar da GIL === |
=== Usando threads apesar da GIL === |
||
− | A GIL impede qualquer paralelismo no código escrito em Python, mas as funcionalidades da plataforma (interpretador, bibliotecas, API do sistema operacional) não são |
+ | A GIL impede qualquer paralelismo no código escrito em Python, mas as funcionalidades da plataforma (interpretador, bibliotecas, API do sistema operacional) não são limitadas pela GIL. '''Todas as chamadas da biblioteca padrão de Python que fazem I/O de rede ou arquivos liberam a GIL.''' Portanto, em qualquer processamento onde o limitante é o I/O (''I/O bound'') o uso de threads em Python pode acelerar muito o processamento em relação a uma solução sequencial que vai passar a maior parte do tempo aguardando I/O. Mas para processamentos limitados pela CPU (''CPU bound'') usar threads em Python atrapalha mais do que ajuda. Importante notar que bibliotecas externas escritas em C para uso com Python podem liberar a GIL, e podem usar threads nativas executando em paralelo. |
+ | |||
+ | === Porque a GIL existe === |
||
+ | |||
+ | A GIL foi criada para simplificar o código em C do interpretador e facilitar a intergração de bibliotecas externas em C que não são thread-safe. Uma razão do sucesso de Python é a imensa varidedade de bibliotecas externas disponíveis, e sem GIL seria muito mais complicado e caro fazer essas integrações. O interpretador padrão de Ruby (MRI) tem também uma ''Global VM Lock'', pelos mesmos motivos. As implementações de Python e Ruby em Java (Jython e JRuby) não têm uma trava global. |
||
+ | |||
+ | == Como fazer concorrência sem paralelismo == |
||
+ | |||
+ | O segredo é não deixar seu programa jamais esperar por uma resposta na rede. Isso pode ser feito de duas formas: |
||
+ | |||
+ | * Com threads, pois as funções de IO da biblioteca padrão de Python liberam a GIL enquanto esperam, permitindo que outra thread avance. Para isso se usa o [https://docs.python.org/3/library/threading.html módulo threading]. |
||
+ | |||
+ | * Com programação orientada a eventos, onde cada função de IO aceita uma função de callback como argumento, e este callback é invocado quando o dispositivo de IO está pronto para enviar/receber dados, ou já enviou/recebeu dados. Para isso se usa o módulo [https://docs.python.org/3/library/asyncio.html asyncio]. |
||
+ | |||
+ | As duas formas podem ser chamadas de '''programação assíncrona''', porque as requisições e respostas podem acontecer fora de ordem. Por exemplo, se um programa usando threads ou <code>asyncio</code> iniciar várias requisições HTTP, as respostas não virão necessariamente mesma ordem, e serão tratadas na ordem em que chegarem. Em contraste, num programa síncrono com apenas uma thread, cada requisição fará o programa parar para aguardar a resposta correspondente, e uma nova requisição será feita somente quando a resposta anterior for recebida. |
||
+ | |||
+ | Porém na prática o termo '''programação assíncrona''' é aplicado mais frequentemente só para a programação orientada a eventos. |
||
+ | |||
+ | |||
+ | [[Categoria:Concorrência]][[Categoria:Python]] |
Edição atual tal como às 15h29min de 28 de janeiro de 2015
Prática
Veja: Oficina de Concorrência em Python
Teoria
Concorrência x paralelismo
Concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once.
Rob Pike, co-inventor da linguagem Go -- Concurrency is not Parallelism (it's better)
Rob Pike (citado acima) define assim em termos bem simples: "Concorrência é lidar com várias coisas ao mesmo tempo. Paralelismo é fazer várias coisas ao mesmo tempo." O importante na concorrência é manter várias atividades simultâneas ativas, mesmo que só uma avance de cada vez.
Exemplo culinário
Ao preparar vários pratos ao mesmo tempo o cozinheiro está trabalhando de forma concorrente, porque normalmente ele só cuida de um prato por vez, mas vários estão sendo preparados ao mesmo tempo. Por outro lado, um fogão de 6 bocas permite aquecer 6 panelas em paralelo. Ou seja, o cozinheiro trabalha em modo concorrente e o fogão em modo paralelo.
Sobre Concorrência em Python
Ao contrário de Go, Python não foi originalmente projetada com foco em programação concorrente, muito menos paralela. O modo tradicional de programar concorrência em Python -- threads -- é limitado no interpretador padrão (CPython) por uma trava global (a GIL) que impede a execução paralela de threads escritas em Python. Isso significa que threads em Python são úteis apenas em aplicações I/O bound -- onde o gargalo está no I/O, como é o caso de aplicações na Internet.
Usando threads apesar da GIL
A GIL impede qualquer paralelismo no código escrito em Python, mas as funcionalidades da plataforma (interpretador, bibliotecas, API do sistema operacional) não são limitadas pela GIL. Todas as chamadas da biblioteca padrão de Python que fazem I/O de rede ou arquivos liberam a GIL. Portanto, em qualquer processamento onde o limitante é o I/O (I/O bound) o uso de threads em Python pode acelerar muito o processamento em relação a uma solução sequencial que vai passar a maior parte do tempo aguardando I/O. Mas para processamentos limitados pela CPU (CPU bound) usar threads em Python atrapalha mais do que ajuda. Importante notar que bibliotecas externas escritas em C para uso com Python podem liberar a GIL, e podem usar threads nativas executando em paralelo.
Porque a GIL existe
A GIL foi criada para simplificar o código em C do interpretador e facilitar a intergração de bibliotecas externas em C que não são thread-safe. Uma razão do sucesso de Python é a imensa varidedade de bibliotecas externas disponíveis, e sem GIL seria muito mais complicado e caro fazer essas integrações. O interpretador padrão de Ruby (MRI) tem também uma Global VM Lock, pelos mesmos motivos. As implementações de Python e Ruby em Java (Jython e JRuby) não têm uma trava global.
Como fazer concorrência sem paralelismo
O segredo é não deixar seu programa jamais esperar por uma resposta na rede. Isso pode ser feito de duas formas:
- Com threads, pois as funções de IO da biblioteca padrão de Python liberam a GIL enquanto esperam, permitindo que outra thread avance. Para isso se usa o módulo threading.
- Com programação orientada a eventos, onde cada função de IO aceita uma função de callback como argumento, e este callback é invocado quando o dispositivo de IO está pronto para enviar/receber dados, ou já enviou/recebeu dados. Para isso se usa o módulo asyncio.
As duas formas podem ser chamadas de programação assíncrona, porque as requisições e respostas podem acontecer fora de ordem. Por exemplo, se um programa usando threads ou asyncio
iniciar várias requisições HTTP, as respostas não virão necessariamente mesma ordem, e serão tratadas na ordem em que chegarem. Em contraste, num programa síncrono com apenas uma thread, cada requisição fará o programa parar para aguardar a resposta correspondente, e uma nova requisição será feita somente quando a resposta anterior for recebida.
Porém na prática o termo programação assíncrona é aplicado mais frequentemente só para a programação orientada a eventos.