Skip to content

Commit d4bd41b

Browse files
committed
cap20: revisão de estilo rolando
1 parent 75cb0e0 commit d4bd41b

File tree

1 file changed

+147
-58
lines changed

1 file changed

+147
-58
lines changed

online/cap20.adoc

Lines changed: 147 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -19,58 +19,70 @@ ____
1919

2020

2121
Este((("concurrent executors", "purpose of"))) capítulo se concentra nas subclasses
22-
de `concurrent.futures.Executor`, que encapsulam o modelo de "disparar um monte
23-
de threads independentes e coletar os resultados em uma fila" descrito por
24-
Michele Simionato.
25-
Executores concorrentes automatizam este modelo,
22+
de `concurrent.futures.Executor`, que incorporam o modelo descrito por
23+
Michele Simionato: "disparar um monte
24+
de threads independentes e coletar os resultados em uma fila".
25+
Executores concorrentes implementam internamente este modelo,
2626
não apenas com threads mas também com processos—que oferecem melhor desempenho
2727
em tarefas de processamento intensivas no uso de CPUs.
2828

2929
Também((("futures", "definition of term"))) introduzo aqui o conceito de
30-
_futures_—objetos que representam a execução assíncrona de uma operação, similares às _promises_ de JavaScript.
31-
Essa ideia básica é a fundação de `concurrent.futures` bem como do pacote `asyncio`, assunto do <<ch_async>>.
30+
_futures_—objetos que representam a execução assíncrona de uma operação,
31+
similares aos _promises_ de JavaScript.
32+
Esta ideia é a fundação de `concurrent.futures`
33+
bem como do pacote `asyncio`, assunto do <<ch_async>>.
3234

3335

3436
=== Novidades neste capítulo
35-
Renomeei((("concurrent executors", "significant changes to"))) este capítulo de "Concorrência com futures" para "Executores concorrentes", porque os executores são o recurso de alto nível mais importante tratado aqui.
37+
Mudei((("concurrent executors", "significant changes to"))) o título deste capítulo de
38+
"Concorrência com futures" para "Executores concorrentes",
39+
porque os executores são o recurso de alto nível mais importante tratado aqui.
3640
_Futures_ são objetos de baixo nível, tratados na <<where_futures_sec>>,
3741
mas quase invisíveis no resto do capítulo.
3842

39-
Todos os exemplos de clientes HTTP agora usam a nova biblioteca https://fpy.li/httpx[_HTTPX_], que oferece APIs síncronas e assíncronas.
43+
Todos os exemplos de clientes HTTP agora usam a biblioteca
44+
https://fpy.li/httpx[_HTTPX_], que oferece APIs síncronas e assíncronas.
4045

41-
A configuração para os experimentos na <<flags2_sec>> ficou mais simples,
42-
graças ao servidor de múltiplas threads adicionado ao pacote https://fpy.li/20-2[`http.server`] no Python 3.7.
43-
Antes, a biblioteca padrão oferecia apenas o `BaseHttpServer` de thread única,
44-
que não era adequado para experiências com clientes concorrentes,
45-
então na primeira edição desse livro precisei usar um servidor externo.
46+
Simplifiquei a configuração para os experimentos na <<flags2_sec>>,
47+
porque desde o Python 3.7 o pacote https://fpy.li/20-2[`http.server`]
48+
é _multi-thread_. Antes, o `http.server` só usava uma thread,
49+
então não servia para experimentos com clientes concorrentes,
50+
o que me obrigou a usar um servidor externo na primeira edição.
4651

47-
A <<launching_processes_sec>> agora demonstra como um executor simplifica o código que vimos na <<code_for_multicore_prime_sec>>.
52+
A <<launching_processes_sec>> agora demonstra como um executor simplifica o
53+
código que vimos na <<code_for_multicore_prime_sec>>.
4854

49-
Por fim, movi a maior parte da teoria para o novo <<ch_concurrency_models>>.
55+
Por fim, movi a maior parte da teoria para o <<ch_concurrency_models>>,
56+
_Modelos de concorrência em Python_.
5057

5158
[[ex_web_downloads_sec]]
5259
=== Downloads concorrentes da web
5360

54-
A((("network I/O", "essential role of concurrency in", id="IOconcur20")))((("concurrent executors", "concurrent web downloads", id="CEwebdown20"))) concorrência é essencial para uma comunicação eficiente via rede: em vez de esperar de braços cruzados por respostas de máquinas remotas, a aplicação deveria fazer alguma outra coisa até a resposta chegar.footnote:[Especialmente se o seu provedor de serviços na nuvem aluga máquinas por tempo de uso, independente de quão ocupada esteja a CPU.]
61+
A((("network I/O", "essential role of concurrency in",
62+
id="IOconcur20")))((("concurrent executors", "concurrent web downloads",
63+
id="CEwebdown20"))) concorrência é essencial para uma comunicação eficiente via
64+
rede: em vez de esperar de braços cruzados por respostas de máquinas remotas, a
65+
aplicação pode fazer outra coisa enquanto a resposta não
66+
chega.
5567

56-
Para demonstrar com código, escrevi três programas simples que baixam da web imagens de 20 bandeiras de países.
68+
Para demonstrar, escrevi três programas simples que baixam da web imagens de 20 bandeiras de países.
5769
O primeiro, _flags.py_, roda sequencialmente:
5870
ele só requisita a imagem seguinte quando a anterior foi baixada e salva localmente.
5971
Os outros dois scripts fazem downloads concorrentes:
6072
eles requisitam várias imagens quase ao mesmo tempo, e as salvam conforme chegam.
6173
O script _flags_threadpool.py_ usa o pacote `concurrent.futures`,
6274
enquanto _flags_asyncio.py_ usa `asyncio`.
6375

64-
O <<ex_flags_sample_runs>> mostra o resultado da execução dos três scripts, três vezes cada um.
65-
6676
Os scripts baixam imagens de _https://fluentpython.com_, que usa uma CDN
67-
(_Content Delivery Network_, _Rede de Fornecimento de Conteúdo_),
68-
então você pode notar os resultados mais lentos nas primeiras passagens.
69-
Os resultados no <<ex_flags_sample_runs>> foram obtidos após várias execuções,
70-
então o cache da CDN estava carregado.
77+
(_Content Delivery Network_, Rede de Entrega de Conteúdo),
78+
então você pode observar resultados mais lentos nos primeiros testes.
79+
Os resultados no <<ex_flags_sample_runs>> foram obtidos após vários testes,
80+
então o cache da CDN estava "quente" (carregado com os dados).
81+
82+
O <<ex_flags_sample_runs>> mostra o resultado da execução dos três scripts, três vezes cada um.
7183

7284
[[ex_flags_sample_runs]]
73-
.Três execuções típicas dos scripts flags.py, flags_threadpool.py, e flags_asyncio.py
85+
.Saida dos scripts flags.py, flags_threadpool.py, e flags_asyncio.py
7486
====
7587
[source, text]
7688
----
@@ -103,38 +115,72 @@ RU IN ID DE BR VN PK MX US IR ET EG NG BD FR CN JP PH CD TR <5>
103115
20 flags downloaded in 1.42s
104116
----
105117
====
106-
<1> A saída de cada execução começa com os códigos dos países de cada bandeira a medida que as imagens são baixadas, e termina com uma mensagem mostrando o tempo decorrido.
118+
119+
<1> A saída de cada execução começa com os códigos dos países de cada bandeira a medida que as imagens são baixadas, e termina com uma mensagem mostrando o tempo decorrido. Note que as siglas dos países estão em ordem alfabética, para podermos comparar com a ordem dos resultados nas versões concorrentes.
120+
107121
<2> _flags.py_ precisou em média de 7,18s para baixar 20 imagens.
122+
108123
<3> A média para _flags_threadpool.py_ foi 1,40s.
124+
109125
<4> Já _flags_asyncio.py_, obteve um tempo médio de 1,35s.
126+
110127
<5> Observe a ordem do códigos de país: nos scripts concorrentes, as imagens foram baixadas em um ordem diferente a cada vez.
111128

112-
A diferença de desempenho entre os scripts concorrentes não é significativa, mas ambos são mais de cinco vezes mais rápidos que o script sequencial—e isto apenas para a pequena tarefa de baixar 20 arquivos, cada um com uns poucos kilobytes.
113-
Se você escalar a tarefa para centenas de downloads, os scripts concorrentes podem superar o código sequencial por um fator de 20 ou mais.
129+
As versões concorrentes são mais de cinco vezes mais rápidas que o script sequencial,
130+
mas entre as versões concorrentes não há diferença significativa de desempenho.
131+
Nestes exemplos, a tarefa é baixar 20 arquivos, com poucos kilobytes cada um.
132+
Se você escalar a tarefa para centenas de downloads,
133+
os scripts concorrentes podem superar o código sequencial por um fator de 20 ou mais.
114134

115135
[WARNING]
116136
====
117-
Ao testar clientes HTTP concorrentes usando servidores web públicos, você pode inadvertidamente lançar um ataque de negação de serviço (DoS, _Denial of Service attack_), ou se tornar suspeito de estar tentando um ataque.
118-
No caso do <<ex_flags_sample_runs>> não há problema, pois aqueles scripts estão codificados para realizar apenas 20 requisições.
119-
Mais adiante nesse capítulo usaremos o pacote `http.server` de Python para executar nossos testes localmente.
137+
138+
Ao testar clientes HTTP concorrentes usando servidores web públicos, você pode
139+
acidentalmente lançar um ataque de negação de serviço (DoS, _Denial of Service_,
140+
negação de serviço),
141+
ou se tornar suspeito de tentar um ataque. No caso do
142+
<<ex_flags_sample_runs>> não há problema, pois aqueles scripts estão codificados
143+
para realizar apenas 20 requisições. Mais adiante neste capítulo usaremos o
144+
pacote `http.server` de Python para executar localmente outros testes com
145+
centenas de requisições.
146+
120147
====
121148

122-
Vamos agora estudar as implementações de dois dos scripts testados no <<ex_flags_sample_runs>>: _flags.py_ e _flags_threadpool.py_. Vou deixar o terceiro, _flags_asyncio.py_, para o <<ch_async>>, mas queria demonstrar os três juntos para fazer duas observações:
149+
Vamos agora estudar as implementações de dois dos scripts testados no
150+
<<ex_flags_sample_runs>>: _flags.py_ e _flags_threadpool.py_. Vou deixar o
151+
terceiro, _flags_asyncio.py_, para o <<ch_async>>, mas queria demonstrar os três
152+
juntos para fazer duas observações:
153+
154+
. Independente dos elementos de concorrência que você use—threads ou
155+
corrotinas—haverá um ganho enorme de desempenho sobre código sequencial em
156+
operações de E/S de rede, se o script for escrito corretamente.
123157

124-
. Independente dos elementos de concorrência que você use—threads ou corrotinas—haverá um ganho enorme de desempenho sobre código sequencial em operações de E/S de rede, se o script for escrito corretamente.
125-
. Para clientes HTTP que podem controlar quantas requisições eles fazem, não há diferenças significativas de desempenho entre threads e corrotinas.footnote:[Para servidores que podem ser acessados por muitos clientes, há uma diferença: as corrotinas escalam melhor, pois usam menos memória que as threads, e também reduzem o custo das mudanças de contexto, que mencionei na <<thread_non_solution_sec>>.]
158+
. Para clientes HTTP que podem controlar quantas requisições eles fazem, não há
159+
diferenças significativas de desempenho entre threads e
160+
corrotinas.footnote:[Para servidores que podem receber requisicões de muitos
161+
clientes, há uma diferença: as corrotinas escalam melhor, pois usam menos
162+
memória que as threads, e também reduzem o custo das mudanças de contexto, que
163+
mencionei na <<thread_non_solution_sec>>.]
126164

127-
Vamos ver o código.((("", startref="IOconcur20")))
165+
Agora vamos ao código.((("", startref="IOconcur20")))
128166

129167

130168
==== Um script de download sequencial
131169

132-
O <<flags_module_ex>> contém((("network I/O", "sequential download script", id="IOsequen20"))) a implementação de _flags.py_, o primeiro script que rodamos no <<ex_flags_sample_runs>>.
133-
Não é muito interessante, mas vamos reutilizar a maior parte do código e das configurações para implementar os scripts concorrentes, então ele merece alguma atenção.
170+
O <<flags_module_ex>> contém((("network I/O", "sequential download script",
171+
id="IOsequen20"))) a implementação de _flags.py_, o primeiro script que rodamos
172+
no <<ex_flags_sample_runs>>. Não é muito interessante, mas vamos reutilizar a
173+
maior parte do código e das configurações para implementar os scripts
174+
concorrentes, então ele merece alguma atenção.
134175

135176
[NOTE]
136177
====
137-
Por clareza, não há qualquer tipo de tratamento de erro no <<flags_module_ex>>. Vamos lidar come exceções mais tarde, mas aqui quero me concentrar na estrutura básica do código, para facilitar a comparação deste script com os scripts que usam concorrência.
178+
179+
Por uma questão didática, o <<flags_module_ex>> não faz tratamento de erros.
180+
Vamos tratar exceções em outros exemplos, mas agora vamos focar na
181+
estrutura básica do código, para facilitar a comparação deste script com os
182+
scripts que usam concorrência.
183+
138184
====
139185

140186
[[flags_module_ex]]
@@ -145,42 +191,85 @@ Por clareza, não há qualquer tipo de tratamento de erro no <<flags_module_ex>>
145191
include::../code/20-executors/getflags/flags.py[tags=FLAGS_PY]
146192
----
147193
====
148-
<1> Importa a biblioteca `httpx`. Ela não é parte da biblioteca padrão. Assim, por convenção, a importação aparece após os módulos da biblioteca padrão e uma linha em branco.
149-
<2> Lista do código de país ISO 3166 para os 20 países mais populosos, em ordem decrescente de população.
150-
<3> O diretório com as imagens das bandeiras.footnote:[As imagens são originalmente do https://fpy.li/20-4[CIA World Factbook], uma publicação de domínio público do governo norte-americano. Copiei as imagens para o meu site, para evitar o risco de lançar um ataque de DoS contra _cia.gov_.]
194+
195+
<1> Importa a biblioteca `httpx`. Ela não é parte da biblioteca padrão. Assim,
196+
por convenção, a importação aparece após os módulos da biblioteca padrão e uma
197+
linha em branco.
198+
199+
<2> Lista do código de país ISO 3166 para os 20 países mais populosos, em ordem
200+
decrescente de população.
201+
202+
<3> O diretório com as imagens das bandeiras.footnote:[As imagens são
203+
originalmente do https://fpy.li/20-4[CIA World Factbook], uma publicação de
204+
domínio público do governo norte-americano. Copiei as imagens para o meu site,
205+
para evitar o risco de lançar um ataque de DoS contra _cia.gov_.]
206+
151207
<4> Diretório local onde as imagens são salvas.
152-
<5> Salva os bytes de `img` para `filename` no `DEST_DIR`.
153-
<6> Dado um código de país, constrói a URL e baixa a imagem, retornando o conteúdo binário da resposta.
154-
<7> É uma boa prática adicionar um timeout razoável para operações de rede, para evitar ficar bloqueado sem motivo por vários minutos.
155-
<8> Por default, o _HTTPX_ não segue redirecionamentos.footnote:[Definir `follow_redirects=True` não é necessário nesse exemplo, mas eu queria destacar essa importante diferença entre _HTTPX_ e _requests_. Além disso, definir `follow_redirects=True` nesse exemplo me dá a flexibilidade de armazenar os arquivos de imagem em outro lugar no futuro. Acho sensata a configuração default do _HTTPX_, pass:[<code>follow_redirects&#x200b;=False</code>], pois redirecionamentos inesperados podem mascarar requisições desnecessárias e complicar o diagnóstico de erro.]
156-
<9> Não há tratamento de erros nesse script, mas esse método lança uma exceção se o status do HTTP não está na faixa 2XX—algo mutio recomendado para evitar falhas silenciosas.
157-
<10> `download_many` é a função chave para comparar com as implementações concorrentes.
158-
<11> Percorre a lista de códigos de país em ordem alfabética, para facilitar a confirmação de que a ordem é preservada na saída; retorna o número de códigos de país baixados.
159-
<12> Mostra um código de país por vez na mesma linha, para vermos o progresso a cada download.
160-
O argumento `end=' '` substitui a costumeira quebra no final de cada linha escrita com um espaço, assim todos os códigos de país aparecem progressivamente na mesma linha.
161-
O argumento `flush=True` é necessário porque, por default, a saída de Python usa um buffer de linha, o que significa que Python só mostraria os caracteres enviados após uma quebra de linha.
162-
<13> `main` precisa ser chamada com a função que fará os downloads; dessa forma podemos usar `main` como uma função de biblioteca com outras implementações de `download_many` nos exemplos de `threadpool` e `ascyncio`.
208+
209+
<5> Salva os bytes de `img` em `filename` no `DEST_DIR`.
210+
211+
<6> Dado um código de país, constrói a URL e baixa a imagem, retornando o
212+
conteúdo binário da resposta.
213+
214+
<7> É uma boa prática adicionar um timeout razoável para operações de rede, para
215+
evitar ficar bloqueado sem motivo por vários minutos.
216+
217+
<8> Por default, o _HTTPX_ não segue redirecionamentos.footnote:[Definir
218+
`follow_redirects=True` não é necessário nesse exemplo, mas eu queria destacar
219+
essa importante diferença entre _HTTPX_ e _requests_. Além disso, definir
220+
`follow_redirects=True` nesse exemplo me dá a flexibilidade de armazenar os
221+
arquivos de imagem em outro lugar no futuro. Acho sensata a configuração default
222+
do _HTTPX_, pass:[<code>follow_redirects&#x200b;=False</code>], pois
223+
redirecionamentos inesperados podem mascarar requisições desnecessárias e
224+
complicar o diagnóstico de erro.]
225+
226+
<9> Não há tratamento de erros neste script, mas o método `.raise_for_status`
227+
lança uma exceção se o status da resposta HTTP não está na faixa 2XX—recomendado para evitar
228+
falhas silenciosas.
229+
230+
<10> `download_many` é a função chave para comparar com as implementações
231+
concorrentes.
232+
233+
<11> Percorre a lista de códigos de país em ordem alfabética, para facilitar a
234+
confirmação de que a ordem é preservada na saída; retorna o número de códigos de
235+
país baixados.
236+
237+
<12> Mostra um código de país por vez na mesma linha, para vermos o progresso a
238+
cada download. O argumento `end=' '` substitui a quebra de linha no final de
239+
cada `print` por um espaço, assim todos os códigos de país aparecem
240+
na mesma linha. O argumento `flush=True` é necessário porque,
241+
por default, o Python usa um buffer de linha na saída padrão,
242+
então só após uma quebra de linha o resultado seria visível no terminal.
243+
A opção `flush=True` força o esvaziamento do buffer,
244+
então podemos ver as siglas aparecendo progressivamente.
245+
246+
<13> `main` precisa ser invocada passando a função que fará os downloads; assim
247+
podemos usar `main` como uma função de biblioteca com outras implementações de
248+
`download_many` nos exemplos de `threadpool` e `ascyncio`.
249+
163250
<14> Cria o `DEST_DIR` se necessário; não acusa erro se o diretório existir.
164-
<15> Mede e apresenta o tempo decorrido após rodar a função `downloader`.
165-
<16> Chama `main` com a função `download_many`.
251+
252+
<15> Registra e apresenta o tempo decorrido após rodar a função `downloader`.
253+
254+
<16> Invoca `main` com a função `download_many`.
166255

167256
[TIP]
168257
====
169258
A biblioteca https://fpy.li/httpx[_HTTPX_] é inspirada no pacote pythônico
170259
https://fpy.li/20-5[_requests_],
171260
mas foi desenvolvida sobre bases mais modernas.
172-
Especialmente, _HTTPX_ tem APIs síncronas e assíncronas,
173-
então podemos usá-la em todos os exemplos de clientes HTTP nesse capítulo e no próximo.
174-
A biblioteca padrão de Python contém o módulo `urllib.request`,
175-
mas sua API é exclusivamente síncrona, e não é muito amigável.
261+
Em particular, _HTTPX_ oferece APIs síncronas e assíncronas,
262+
então podemos usá-la em todos os exemplos de clientes HTTP neste capítulo e no próximo.
263+
A biblioteca padrão do Python contém o módulo `urllib.request`,
264+
mas sua API é somente síncrona, e não é nada amigável.
176265
====
177266

178267
Não há mesmo nada de novo em _flags.py_.
179268
Ele serve de base para comparação com outros scripts, e o usei como uma biblioteca, para evitar código redundante ao implementar aqueles scripts.
180269
Vamos ver agora uma reimplementação usando `concurrent.futures`.((("", startref="IOsequen20")))
181270

182271
[[downloading_with_futures_sec]]
183-
==== Download com concurrent.futures
272+
==== Download com `concurrent.futures`
184273

185274
Os((("network I/O", "downloading with concurrent.futures", id="IOfutures20")))((("concurrent.futures", "downloading with", id="confut20"))) principais recursos do pacote `concurrent.futures` são as classes `ThreadPoolExecutor` e `ProcessPoolExecutor`, que implementam uma API para submissão de _callables_ (_"chamáveis"_) para execução em um banco de threads ou processos.
186275
As classes gerenciam de forma transparente um banco de threads ou processos de trabalho, e filas para distribuição de tarefas e coleta de resultados.

0 commit comments

Comments
 (0)