From af2d0211b4068f293c624a3424fb5b0416d9375b Mon Sep 17 00:00:00 2001 From: Giovana Morais Date: Thu, 26 Sep 2019 15:14:52 -0300 Subject: [PATCH 01/16] =?UTF-8?q?tradu=C3=A7=C3=A3o:=20traduzindo=20maior?= =?UTF-8?q?=20parte=20de=20sync.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- primeiros-passos-com-go/sync.md | 233 +++++++++++++++++++------------- 1 file changed, 138 insertions(+), 95 deletions(-) diff --git a/primeiros-passos-com-go/sync.md b/primeiros-passos-com-go/sync.md index 965b1b64..4af5c36f 100644 --- a/primeiros-passos-com-go/sync.md +++ b/primeiros-passos-com-go/sync.md @@ -1,110 +1,117 @@ # Sync -[**You can find all the code for this chapter here**](https://github.com/quii/learn-go-with-tests/tree/master/sync) +[**Você pode encontrar todo o código para esse capítulo aqui**](https://github.com/quii/learn-go-with-tests/tree/master/sync) -We want to make a counter which is safe to use concurrently. +Queremos fazer um contador que é seguro para se usar concorrentemente. -We'll start with an unsafe counter and verify its behaviour works in a single-threaded environment. +Vamos começar com um contador inseguro e verificar se seu comportamento funciona em um ambiente com apenas uma thread. -Then we'll exercise it's unsafeness with multiple goroutines trying to use it via a test and fix it. +Depois vamos exercitar sua insegurança com múltiplas *goroutines* tentando usar +ele via teste e consertar essa falha. -## Write the test first +## Escreva o teste primeiro -We want our API to give us a method to increment the counter and then retrieve its value. +Queremos que nossa API nos dê um método para incrementar o contador e depois recuperar esse valor. ```go -func TestCounter(t *testing.T) { +func TesteContador(t *testing.T) { t.Run("incrementing the counter 3 times leaves it at 3", func(t *testing.T) { - counter := Counter{} + counter := Contador{} counter.Inc() counter.Inc() counter.Inc() - if counter.Value() != 3 { + if counter.Value() != 3 { t.Errorf("got %d, want %d", counter.Value(), 3) } }) } ``` -## Try to run the test +## Tente rodar o teste ```text -./sync_test.go:9:14: undefined: Counter +./sync_test.go:9:14: undefined: Contador ``` -## Write the minimal amount of code for the test to run and check the failing test output +## Escreva a quantidade mínima de código para o teste rodar e cheque a saída que falhou dele -Let's define `Counter`. +Vamos definir `Contador`. ```go -type Counter struct { +type Contador struct { } ``` -Try again and it fails with the following +Tente de novo e ele falhará com o seguinte ```text -./sync_test.go:14:10: counter.Inc undefined (type Counter has no field or method Inc) -./sync_test.go:18:13: counter.Value undefined (type Counter has no field or method Value) +./sync_test.go:14:10: counter.Inc undefined (type Contador has no field or method Inc) +./sync_test.go:18:13: counter.Value undefined (type Contador has no field or method Value) ``` -So to finally make the test run we can define those methods +Então, pra finalmente fazer o teste rodar, podemos definir esses métodos ```go -func (c *Counter) Inc() { +func (c *Contador) Inc() { } -func (c *Counter) Value() int { +func (c *Contador) Value() int { return 0 } ``` -It should now run and fail +Agora tudo deve rodar e falhar ```text -=== RUN TestCounter -=== RUN TestCounter/incrementing_the_counter_3_times_leaves_it_at_3 ---- FAIL: TestCounter (0.00s) - --- FAIL: TestCounter/incrementing_the_counter_3_times_leaves_it_at_3 (0.00s) +=== RUN TesteContador +=== RUN TesteContador/incrementing_the_counter_3_times_leaves_it_at_3 +--- FAIL: TesteContador (0.00s) + --- FAIL: TesteContador/incrementing_the_counter_3_times_leaves_it_at_3 (0.00s) sync_test.go:27: got 0, want 3 ``` -## Write enough code to make it pass +## Escreva código o suficiente para fazer ele passar + +Isso deve ser trivial para experts em Go como nós. Precisamos manter algum +estado do contador no nosso datatype e daí incrementá-lo em cada chamada do +`Inc`. -This should be trivial for Go experts like us. We need to keep some state for the counter in our datatype and then increment it on every `Inc` call ```go -type Counter struct { +type Contador struct { value int } -func (c *Counter) Inc() { +func (c *Contador) Inc() { c.value++ } -func (c *Counter) Value() int { +func (c *Contador) Value() int { return c.value } ``` -## Refactor +## Refatoração + +Não há muito o que refatorar, mas, dado que iremos escrever mais testes em +torno do `Contador`, vamos escrever uma pequena função de asserção `assertCount` +para que o teste fique um pouco mais legível. -There's not a lot to refactor but given we're going to write more tests around `Counter` we'll write a small assertion function `assertCount` so the test reads a bit clearer. ```go t.Run("incrementing the counter 3 times leaves it at 3", func(t *testing.T) { - counter := Counter{} + counter := Contador{} counter.Inc() counter.Inc() counter.Inc() - assertCounter(t, counter, 3) + assertContador(t, counter, 3) }) -func assertCounter(t *testing.T, got Counter, want int) { +func assertContador(t *testing.T, got Contador, want int) { t.Helper() if got.Value() != want { t.Errorf("got %d, want %d", got.Value(), want) @@ -112,16 +119,18 @@ func assertCounter(t *testing.T, got Counter, want int) { } ``` -## Next steps +## Próximos passos -That was easy enough but now we have a requirement that it must be safe to use in a concurrent environment. We will need to write a failing test to exercise this. +Isso foi muito fácil, mas agora nós agora temos uma requisição que é: ele precisa +ser seguro o suficiente para usar em um ambiente com acesso concorrente. Vamos precisar +criar um teste pra exercitar isso. -## Write the test first +## Escreva o teste primeiro ```go t.Run("it runs safely concurrently", func(t *testing.T) { wantedCount := 1000 - counter := Counter{} + counter := Contador{} var wg sync.WaitGroup wg.Add(wantedCount) @@ -134,143 +143,177 @@ t.Run("it runs safely concurrently", func(t *testing.T) { } wg.Wait() - assertCounter(t, counter, wantedCount) + assertContador(t, counter, wantedCount) }) ``` -This will loop through our `wantedCount` and fire a goroutine to call `counter.Inc()`. +Isso vai iterar pelo nosso `wantedCount` e chamar uma *goroutine* pra chamar `counter.Inc()`. -We are using [`sync.WaitGroup`](https://golang.org/pkg/sync/#WaitGroup) which is a convenient way of synchronising concurrent processes. +Nós estamos usando [`sync.WaitGroup`](https://golang.org/pkg/sync/#WaitGroup) +que é uma maneira conveniente de sincronizar processos concorrentes. -> A WaitGroup waits for a collection of goroutines to finish. The main goroutine calls Add to set the number of goroutines to wait for. Then each of the goroutines runs and calls Done when finished. At the same time, Wait can be used to block until all goroutines have finished. +> Um WaitGroup aguarda por uma coleção *goroutines* terminar seu processamento. +A *goroutine* principal faz a chamada para o Add definir o número de *goroutines* +serão esperadas. Então, cada uma das *goroutines* rodam novamente e chamam Done +quando terminam sua execução. Ao mesmo tempo, Wait pode ser usada para bloquear até +que todas as *goroutines* tenham terminado. -By waiting for `wg.Wait()` to finish before making our assertions we can be sure all of our goroutines have attempted to `Inc` the `Counter`, +Ao esperar por `wg.Wait()` terminar sua execução antes de fazer nossas asserções, nós +podemos ter certeza que todas as nossas *goroutines* tentaram `Inc` o `Contador`. -## Try to run the test +## Tente rodar o teste ```text -=== RUN TestCounter/it_runs_safely_in_a_concurrent_envionment ---- FAIL: TestCounter (0.00s) - --- FAIL: TestCounter/it_runs_safely_in_a_concurrent_envionment (0.00s) +=== RUN TesteContador/it_runs_safely_in_a_concurrent_envionment +--- FAIL: TesteContador (0.00s) + --- FAIL: TesteContador/it_runs_safely_in_a_concurrent_envionment (0.00s) sync_test.go:26: got 939, want 1000 FAIL ``` -The test will _probably_ fail with a different number, but nonetheless it demonstrates it does not work when multiple goroutines are trying to mutate the value of the counter at the same time. +O teste _provavelmente_ vai falhar com um número diferente, mas de toda forma ele demonstra +que não roda com várias *goroutines* estão tentando mudar o valor do contador ao mesmo +tempo. -## Write enough code to make it pass +## Escreva código o suficiente para passar o teste -A simple solution is to add a lock to our `Counter`, a [`Mutex`](https://golang.org/pkg/sync/#Mutex) +Uma solução simples é adicionar uma trava ao nosso `Contador`, um +[`Mutex`](https://golang.org/pkg/sync/#Mutex) -> A Mutex is a mutual exclusion lock. The zero value for a Mutex is an unlocked mutex. +> Um Mutex é uma trava de exclusão mútua. O valor zero de um Mutex é um Mutex destravado. ```go -type Counter struct { +type Contador struct { mu sync.Mutex value int } -func (c *Counter) Inc() { +func (c *Contador) Inc() { c.mu.Lock() defer c.mu.Unlock() c.value++ } ``` -What this means is any goroutine calling `Inc` will acquire the lock on `Counter` if they are first. All the other goroutines will have to wait for it to be `Unlock`ed before getting access. +Isso significa que qualquer *goroutine* chamando `Inc` vai receber a trava em `Contador` +se eles forem o primeiro. Todas as outras *goroutines* vão ter que esperar por ele até +que ele esteja `Unlock`, ou destravado, antes de ganhar o acesso. -If you now re-run the test it should now pass because each goroutine has to wait its turn before making a change. +Agora se você rodar o teste novamente, ele deve funcionar porque cada uma das *goroutines* +têm que esperar até que seja sua vez antes de fazer alguma mudança. -## I've seen other examples where the `sync.Mutex` is embedded into the struct. +## Eu vi outros exemplos nos quais `sync.Mutex` está embutido dentro da struct. -You may see examples like this +Você pode ver exemplos como esse ```go -type Counter struct { +type Contador struct { sync.Mutex value int } ``` -It can be argued that it can make the code a bit more elegant. +É discutido que isso pode tornar o código um pouco mais elegante. ```go -func (c *Counter) Inc() { +func (c *Contador) Inc() { c.Lock() defer c.Unlock() c.value++ } ``` -This _looks_ nice but while programming is a hugely subjective discipline, this is **bad and wrong**. +Isso _parece_ legal, mas enquanto programação é uma disciplina altamente +subjetiva, isso é **feio e errado**. -Sometimes people forget that embedding types means the methods of that type becomes _part of the public interface_; and you often will not want that. Remember that we should be very careful with our public APIs, the moment we make something public is the moment other code can couple themselves to it. We always want to avoid unnecessary coupling. +Às vezes as pessoas esquecem que tipos embutidos significam que os métodos daquele +tipo se tornam _parte da interface pública_; e você geralmente não quer isso. Não se +esqueçam que devemos ser muito cuidados com as nossas APIs públicas. O momento que +tornamos algo público é o momento que outros códigos podem acoplar-se a ele e nós +queremos evitar acoplamentos desnecessários. -Exposing `Lock` and `Unlock` is at best confusing but at worst potentially very harmful to your software if callers of your type start calling these methods. +Expor `Lock` e `Unlock` é no seu melhor muito confuso e no seu pior potencialmente +perigoso para o seu software se quem chamar o seu tipo começar a chamar esses +métodos diretamente. -![Showing how a user of this API can wrongly change the state of the lock](https://i.imgur.com/SWYNpwm.png) +![Demonstração de como um usuário dessa API pode chamar erroneamente o estado da trava](https://i.imgur.com/SWYNpwm.png) -_This seems like a really bad idea_ +_Isso parece como uma ideia muito ruim_ -## Copying mutexes +## Copiando mutexes -Our test passes but our code is still a bit dangerous +Nossos testes passam, mas nosso código ainda é um pouco perigoso. -If you run `go vet` on your code you should get an error like the following +Se você rodar `go vet` no seu código, deve receber um erro similar ao seguinte: ```text -sync/v2/sync_test.go:16: call of assertCounter copies lock value: v1.Counter contains sync.Mutex -sync/v2/sync_test.go:39: assertCounter passes lock by value: v1.Counter contains sync.Mutex +sync/v2/sync_test.go:16: call of assertContador copies lock value: v1.Contador contains sync.Mutex +sync/v2/sync_test.go:39: assertContador passes lock by value: v1.Contador contains sync.Mutex ``` -A look at the documentation of [`sync.Mutex`](https://golang.org/pkg/sync/#Mutex) tells us why +Uma rápida olhada na documentação do [`sync.Mutex`](https://golang.org/pkg/sync/#Mutex) +nos diz o porquê -> A Mutex must not be copied after first use. +> Um Mutex não deve ser copiado depois do primeiro uso. -When we pass our `Counter` \(by value\) to `assertCounter` it will try and create a copy of the mutex. +Quando passamos nosso `Contador` \(por valor\) para `assertContador` ele vai tentar criar uma cópia do mutex. -To solve this we should pass in a pointer to our `Counter` instead, so change the signature of `assertCounter` +Para resolver isso, devemos passar um ponteiro para o nosso `Contador`. Vamos então mudar a assinatura de +`assertContador`. ```go -func assertCounter(t *testing.T, got *Counter, want int) +func assertContador(t *testing.T, got *Contador, want int) ``` -Our tests will no longer compile because we are trying to pass in a `Counter` rather than a `*Counter`. To solve this I prefer to create a constructor which shows readers of your API that it would be better to not initialise the type yourself. +Nossos testes não vão mais compilar porque estamos tentando passar um `Contador` em vez de um `*Contador`. +Para resolver isso, é preferível criar um construtor que mostra aos usuários da nossa API que seria +melhor não inicializar o tipo ele mesmo. + ```go -func NewCounter() *Counter { - return &Counter{} +func NewContador() *Contador { + return &Contador{} } ``` -Use this function in your tests when initialising `Counter`. +Use essa função em seus teste quando for inicializar o `Contador`. -## Wrapping up +## Resumindo -We've covered a few things from the [sync package](https://golang.org/pkg/sync/) +Cobrimos algumas coisas no [pacote sync](https://golang.org/pkg/sync/): -* `Mutex` allows us to add locks to our data -* `Waitgroup` is a means of waiting for goroutines to finish jobs +* `Mutex` que nos permite adicionar travas aos nossos dados +* `Waitgroup` que é uma maneira de esperar as *goroutines* terminar suas tarefas -### When to use locks over channels and goroutines? +### Quando usar travas em vez de *channels* e *goroutines*? -[We've previously covered goroutines in the first concurrency chapter](concurrency.md) which let us write safe concurrent code so why would you use locks? -[The go wiki has a page dedicated to this topic; Mutex Or Channel](https://github.com/golang/go/wiki/MutexOrChannel) +[Anteriormente cobrimos *goroutines* no primeiro capítulo de concorrência](concurrency.md) +que nos permite escrever código concorrente e seguro, então por que usar travas? +[A wiki do go tem uma página dedicada para esse tópico: Mutex ou Channel?](https://github.com/golang/go/wiki/MutexOrChannel) -> A common Go newbie mistake is to over-use channels and goroutines just because it's possible, and/or because it's fun. Don't be afraid to use a sync.Mutex if that fits your problem best. Go is pragmatic in letting you use the tools that solve your problem best and not forcing you into one style of code. +> Um erro comum de um iniciante em Go é usar demais os *channels* e *goroutines* apenas +porque é possível e/ou porque é divertido. Não tenha medo de usar um `sync.Mutex` se +ele se encaixa melhor no seu problema. Go é pragmático em deixar você escolhar as +ferramentas que melhor resolvem o seu problema e não te forçar em um único estilo +de código. Paraphrasing: -* **Use channels when passing ownership of data** -* **Use mutexes for managing state** +* **Use channels quando for passar a propridade de um dado** +* **Use mutexes pra gerenciar estados** ### go vet -Remember to use go vet in your build scripts as it can alert you to some subtle bugs in your code before they hit your poor users. +Não se esqueça de usar `go vet` nos seus scripts de build porque ele pode te alertar a respeito de bugs mais +sutis no seu código antes que eles atinjam seus pobres usuários. -### Dont use embedding because it's convenient +### Não use códigos embutidos apenas porque é conveniente -* Think about the effect embedding has on your public API. -* Do you _really_ want to expose these methods and have people coupling their own code to them? -* With respect to mutexes, this could be potentially disastrous in very unpredictable and weird ways, imagine some nefarious code unlocking a mutex when it shouldn't be; this would cause some very strange bugs that will be hard to track down. +* Pense a respeito do efeito que embutir códigos tem na sua API pública. +* Você _realmente_ quer expor esses métodos e ter pessoas acoplando o código +próprio delas a ele? +* No que diz respeito a mutexes, pode ser potencialmente um desastre de maneiras +muito imprevisíveis e estranhas. Imagine algum código obscuro destravando um +mutex quando não deveria; isso causaria erros muito estranhos e que seriam bastante +difíceis de encontrar. From 32af4d2470efa12f3bad89308dcbf425e979af31 Mon Sep 17 00:00:00 2001 From: Giovana Morais Date: Thu, 3 Oct 2019 00:39:59 -0300 Subject: [PATCH 02/16] =?UTF-8?q?tradu=C3=A7=C3=A3o=20dos=20testes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- primeiros-passos-com-go/sync.md | 92 ++++++++++++++++----------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/primeiros-passos-com-go/sync.md b/primeiros-passos-com-go/sync.md index 4af5c36f..4fd40f06 100644 --- a/primeiros-passos-com-go/sync.md +++ b/primeiros-passos-com-go/sync.md @@ -15,14 +15,14 @@ Queremos que nossa API nos dê um método para incrementar o contador e depois r ```go func TesteContador(t *testing.T) { - t.Run("incrementing the counter 3 times leaves it at 3", func(t *testing.T) { - counter := Contador{} - counter.Inc() - counter.Inc() - counter.Inc() - - if counter.Value() != 3 { - t.Errorf("got %d, want %d", counter.Value(), 3) + t.Run("incrementar o contador 3 vezes o deixa com valor 3", func(t *testing.T) { + contador := Contador{} + contador.Inc() + contador.Inc() + contador.Inc() + + if contador.Valor() != 3 { + t.Errorf("recebido %d, desejado %d", contador.Valor(), 3) } }) } @@ -47,8 +47,8 @@ type Contador struct { Tente de novo e ele falhará com o seguinte ```text -./sync_test.go:14:10: counter.Inc undefined (type Contador has no field or method Inc) -./sync_test.go:18:13: counter.Value undefined (type Contador has no field or method Value) +./sync_test.go:14:10: contador.Inc undefined (type Contador has no field or method Inc) +./sync_test.go:18:13: contador.Valor undefined (type Contador has no field or method Valor) ``` Então, pra finalmente fazer o teste rodar, podemos definir esses métodos @@ -58,7 +58,7 @@ func (c *Contador) Inc() { } -func (c *Contador) Value() int { +func (c *Contador) Valor() int { return 0 } ``` @@ -67,10 +67,10 @@ Agora tudo deve rodar e falhar ```text === RUN TesteContador -=== RUN TesteContador/incrementing_the_counter_3_times_leaves_it_at_3 +=== RUN TesteContador/incrementar_o_contador_3_vezes_o_deixa_com_valor_3 --- FAIL: TesteContador (0.00s) - --- FAIL: TesteContador/incrementing_the_counter_3_times_leaves_it_at_3 (0.00s) - sync_test.go:27: got 0, want 3 + --- FAIL: TesteContador/incrementar_o_contador_3_vezes_o_deixa_com_valor_3 (0.00s) + sync_test.go:27: recebido 0, desejado 3 ``` ## Escreva código o suficiente para fazer ele passar @@ -82,15 +82,15 @@ estado do contador no nosso datatype e daí incrementá-lo em cada chamada do ```go type Contador struct { - value int + valor int } func (c *Contador) Inc() { - c.value++ + c.valor++ } -func (c *Contador) Value() int { - return c.value +func (c *Contador) Valor() int { + return c.valor } ``` @@ -102,19 +102,19 @@ para que o teste fique um pouco mais legível. ```go -t.Run("incrementing the counter 3 times leaves it at 3", func(t *testing.T) { - counter := Contador{} - counter.Inc() - counter.Inc() - counter.Inc() +t.Run("incrementar o contador 3 vezes o deixa com valor 3", func(t *testing.T) { + contador := Contador{} + contador.Inc() + contador.Inc() + contador.Inc() - assertContador(t, counter, 3) + assertContador(t, contador, 3) }) -func assertContador(t *testing.T, got Contador, want int) { +func assertContador(t *testing.T, recebido Contador, desejado int) { t.Helper() - if got.Value() != want { - t.Errorf("got %d, want %d", got.Value(), want) + if recebido.Valor() != desejado { + t.Errorf("recebido %d, quero receber %d", recebido.Valor(), desejado) } } ``` @@ -128,26 +128,26 @@ criar um teste pra exercitar isso. ## Escreva o teste primeiro ```go -t.Run("it runs safely concurrently", func(t *testing.T) { - wantedCount := 1000 - counter := Contador{} +t.Run("roda concorrentemente em segurança", func(t *testing.T) { + contadorDesejado := 1000 + contador := Contador{} var wg sync.WaitGroup - wg.Add(wantedCount) + wg.Add(contadorDesejado) - for i:=0; i Date: Thu, 3 Oct 2019 00:40:26 -0300 Subject: [PATCH 03/16] =?UTF-8?q?tradu=C3=A7=C3=A3o=20das=20vari=C3=A1veis?= =?UTF-8?q?=20e=20de=20mensagens=20de=20teste?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sync/v2/sync.go | 23 ++++++++++++----------- sync/v2/sync_test.go | 34 +++++++++++++++++----------------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/sync/v2/sync.go b/sync/v2/sync.go index a7d8a16f..9e13d716 100644 --- a/sync/v2/sync.go +++ b/sync/v2/sync.go @@ -2,25 +2,26 @@ package v1 import "sync" -// Counter will increment a number -type Counter struct { +// Contador vai incrementar um número +type Contador struct { mu sync.Mutex - value int + valor int } -// NewCounter returns a new Counter -func NewCounter() *Counter { - return &Counter{} +// NovoContador retorna um novo Contador +func NovoContador() *Contador { + return &Contador{} } -// Inc the count -func (c *Counter) Inc() { +// Incrementa o contador +func (c *Contador) Inc() { c.mu.Lock() defer c.mu.Unlock() - c.value++ + c.valor++ } // Value returns the current count -func (c *Counter) Value() int { - return c.value +// Valor retorna o atual contador +func (c *Contador) Valor() int { + return c.valor } diff --git a/sync/v2/sync_test.go b/sync/v2/sync_test.go index 5fdac903..d8af4c8f 100644 --- a/sync/v2/sync_test.go +++ b/sync/v2/sync_test.go @@ -5,40 +5,40 @@ import ( "testing" ) -func TestCounter(t *testing.T) { +func TestContador(t *testing.T) { - t.Run("incrementing the counter 3 times leaves it at 3", func(t *testing.T) { - counter := NewCounter() - counter.Inc() - counter.Inc() - counter.Inc() + t.Run("incrementar o contador 3 vezes o deixa com valor 3", func(t *testing.T) { + contador := NovoContador() + contador.Inc() + contador.Inc() + contador.Inc() - assertCounter(t, counter, 3) + assertContador(t, contador, 3) }) - t.Run("it runs safely concurrently", func(t *testing.T) { - wantedCount := 1000 - counter := NewCounter() + t.Run("roda concorrentemente em seguranca", func(t *testing.T) { + contadorDesejado := 1000 + contador := NovoContador() var wg sync.WaitGroup - wg.Add(wantedCount) + wg.Add(contadorDesejado) - for i := 0; i < wantedCount; i++ { + for i := 0; i < contadorDesejado; i++ { go func(w *sync.WaitGroup) { - counter.Inc() + contador.Inc() w.Done() }(&wg) } wg.Wait() - assertCounter(t, counter, wantedCount) + assertContador(t, contador, contadorDesejado) }) } -func assertCounter(t *testing.T, got *Counter, want int) { +func assertContador(t *testing.T, recebido *Contador, desejado int) { t.Helper() - if got.Value() != want { - t.Errorf("got %d, want %d", got.Value(), want) + if recebido.Value() != desejado { + t.Errorf("recebido %d, desejado %d", recebido.Value(), desejado) } } From e053ea94b976ed66e831e45f2ea577b646a53b06 Mon Sep 17 00:00:00 2001 From: Giovana Morais Date: Fri, 4 Oct 2019 16:36:52 -0300 Subject: [PATCH 04/16] =?UTF-8?q?tradu=C3=A7=C3=A3o:=20arrumando=20nome=20?= =?UTF-8?q?de=20vari=C3=A1vel=20traduzida?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sync/v2/sync_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sync/v2/sync_test.go b/sync/v2/sync_test.go index d8af4c8f..57c61130 100644 --- a/sync/v2/sync_test.go +++ b/sync/v2/sync_test.go @@ -38,7 +38,7 @@ func TestContador(t *testing.T) { func assertContador(t *testing.T, recebido *Contador, desejado int) { t.Helper() - if recebido.Value() != desejado { - t.Errorf("recebido %d, desejado %d", recebido.Value(), desejado) + if recebido.Valor() != desejado { + t.Errorf("recebido %d, desejado %d", recebido.Valor(), desejado) } } From 199994c465d8900e406773531e43df959ebf2f8b Mon Sep 17 00:00:00 2001 From: Giovana Morais Date: Mon, 7 Oct 2019 18:53:52 -0300 Subject: [PATCH 05/16] =?UTF-8?q?traduzindo=20c=C3=B3digos=20do=20v1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sync/v1/sync.go | 16 ++++++++-------- sync/v1/sync_test.go | 20 ++++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/sync/v1/sync.go b/sync/v1/sync.go index 2ed234f1..64e61fa7 100644 --- a/sync/v1/sync.go +++ b/sync/v1/sync.go @@ -1,16 +1,16 @@ package v1 -// Counter will increment a number -type Counter struct { - value int +// Contador will increment a number +type Contador struct { + valor int } // Inc the count -func (c *Counter) Inc() { - c.value++ +func (c *Contador) Inc() { + c.valor++ } -// Value returns the current count -func (c *Counter) Value() int { - return c.value +// Valor returns the current count +func (c *Contador) Valor() int { + return c.valor } diff --git a/sync/v1/sync_test.go b/sync/v1/sync_test.go index 1e318a74..013d983a 100644 --- a/sync/v1/sync_test.go +++ b/sync/v1/sync_test.go @@ -4,21 +4,21 @@ import ( "testing" ) -func TestCounter(t *testing.T) { +func TestContador(t *testing.T) { - t.Run("incrementing the counter 3 times leaves it at 3", func(t *testing.T) { - counter := Counter{} - counter.Inc() - counter.Inc() - counter.Inc() + t.Run("incrementar o contador 3 vezes o deixa com valor 3", func(t *testing.T) { + contador := Contador{} + contador.Inc() + contador.Inc() + contador.Inc() - assertCounter(t, counter, 3) + assertContador(t, contador, 3) }) } -func assertCounter(t *testing.T, got Counter, want int) { +func assertContador(t *testing.T, recebido Contador, desejado int) { t.Helper() - if got.Value() != want { - t.Errorf("got %d, want %d", got.Value(), want) + if recebido.Valor() != desejado { + t.Errorf("recebido %d, desejado %d", recebido.Valor(), desejado) } } From 2a2062a9eb84cddb1438c0b11677b441ae99a28e Mon Sep 17 00:00:00 2001 From: Giovana Morais Date: Mon, 7 Oct 2019 18:54:27 -0300 Subject: [PATCH 06/16] =?UTF-8?q?adicionando=20mudan=C3=A7as=20referentes?= =?UTF-8?q?=20=C3=A0=20primeira=20revis=C3=A3o=20do=20texto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- primeiros-passos-com-go/sync.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/primeiros-passos-com-go/sync.md b/primeiros-passos-com-go/sync.md index 4fd40f06..631f765e 100644 --- a/primeiros-passos-com-go/sync.md +++ b/primeiros-passos-com-go/sync.md @@ -34,7 +34,7 @@ func TesteContador(t *testing.T) { ./sync_test.go:9:14: undefined: Contador ``` -## Escreva a quantidade mínima de código para o teste rodar e cheque a saída que falhou dele +## Escreva a quantidade mínima de código para o teste rodar e verifique a saída que falhou dele Vamos definir `Contador`. @@ -171,9 +171,9 @@ podemos ter certeza que todas as nossas *goroutines* tentaram `Inc` o `Contador` FAIL ``` -O teste _provavelmente_ vai falhar com um número diferente, mas de toda forma ele demonstra -que não roda com várias *goroutines* estão tentando mudar o valor do contador ao mesmo -tempo. +O teste _provavelmente_ vai falhar com um número diferente, mas de toda forma +ele demonstra que não roda corretamente quando várias *goroutines* tentam +mudar o valor do contador ao mesmo tempo. ## Escreva código o suficiente para passar o teste @@ -226,15 +226,15 @@ func (c *Contador) Inc() { Isso _parece_ legal, mas enquanto programação é uma disciplina altamente subjetiva, isso é **feio e errado**. -Às vezes as pessoas esquecem que tipos embutidos significam que os métodos daquele -tipo se tornam _parte da interface pública_; e você geralmente não quer isso. Não se -esqueçam que devemos ser muito cuidados com as nossas APIs públicas. O momento que -tornamos algo público é o momento que outros códigos podem acoplar-se a ele e nós -queremos evitar acoplamentos desnecessários. +Às vezes as pessoas esquecem que tipos embutidos significam que os métodos +daquele tipo se tornam _parte da interface pública_; e você geralmente não +quer isso. Não se esqueçam que devemos ser muito cuidadosos com as nossas APIs +públicas. O momento que tornamos algo público é o momento que outros códigos +podem acoplar-se a ele e nós queremos evitar acoplamentos desnecessários. -Expor `Lock` e `Unlock` é no seu melhor muito confuso e no seu pior potencialmente -perigoso para o seu software se quem chamar o seu tipo começar a chamar esses -métodos diretamente. +Expor `Lock` e `Unlock` é, no seu melhor caso, muito confuso e, no seu pior +caso, potencialmente perigoso para o seu software se quem chamar o seu tipo +começar a chamar esses métodos diretamente. ![Demonstração de como um usuário dessa API pode chamar erroneamente o estado da trava](https://i.imgur.com/SWYNpwm.png) From f6e0a0170c2f1261a53eb585dfb91468a59a8dda Mon Sep 17 00:00:00 2001 From: Giovana Morais Date: Tue, 8 Oct 2019 20:27:52 -0300 Subject: [PATCH 07/16] =?UTF-8?q?descartando=20coment=C3=A1rio=20em=20ingl?= =?UTF-8?q?=C3=AAs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sync/v2/sync.go | 1 - 1 file changed, 1 deletion(-) diff --git a/sync/v2/sync.go b/sync/v2/sync.go index 9e13d716..832b5718 100644 --- a/sync/v2/sync.go +++ b/sync/v2/sync.go @@ -20,7 +20,6 @@ func (c *Contador) Inc() { c.valor++ } -// Value returns the current count // Valor retorna o atual contador func (c *Contador) Valor() int { return c.valor From 2be92a40f0188a4a747b9be061ce77895f6e5477 Mon Sep 17 00:00:00 2001 From: Giovana Morais Date: Tue, 8 Oct 2019 20:28:04 -0300 Subject: [PATCH 08/16] =?UTF-8?q?adicionando=20notas=20da=20revis=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- primeiros-passos-com-go/sync.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/primeiros-passos-com-go/sync.md b/primeiros-passos-com-go/sync.md index 631f765e..ec5ac9fc 100644 --- a/primeiros-passos-com-go/sync.md +++ b/primeiros-passos-com-go/sync.md @@ -1,6 +1,6 @@ # Sync -[**Você pode encontrar todo o código para esse capítulo aqui**](https://github.com/quii/learn-go-with-tests/tree/master/sync) +[**Você pode encontrar todo o código para esse capítulo aqui**](https://github.com/larien/learn-go-with-tests/tree/master/sync) Queremos fazer um contador que é seguro para se usar concorrentemente. @@ -34,7 +34,7 @@ func TesteContador(t *testing.T) { ./sync_test.go:9:14: undefined: Contador ``` -## Escreva a quantidade mínima de código para o teste rodar e verifique a saída que falhou dele +## Escreva a quantidade mínima de código para o teste rodar e verifique a saída do teste que falhou Vamos definir `Contador`. @@ -73,7 +73,7 @@ Agora tudo deve rodar e falhar sync_test.go:27: recebido 0, desejado 3 ``` -## Escreva código o suficiente para fazer ele passar +## Escreva código o suficiente para fazer o teste passar Isso deve ser trivial para experts em Go como nós. Precisamos manter algum estado do contador no nosso datatype e daí incrementá-lo em cada chamada do @@ -121,7 +121,7 @@ func assertContador(t *testing.T, recebido Contador, desejado int) { ## Próximos passos -Isso foi muito fácil, mas agora nós agora temos uma requisição que é: ele precisa +Isso foi muito fácil, mas agora nós temos uma requisição que é: ele precisa ser seguro o suficiente para usar em um ambiente com acesso concorrente. Vamos precisar criar um teste pra exercitar isso. @@ -147,16 +147,16 @@ t.Run("roda concorrentemente em segurança", func(t *testing.T) { }) ``` -Isso vai iterar pelo nosso `contadorDesejado` e chamar uma *goroutine* pra chamar `contador.Inc()`. +Isso vai iterar pelo nosso `contadorDesejado` e disparar uma *goroutine* pra chamar `contador.Inc()`. Nós estamos usando [`sync.WaitGroup`](https://golang.org/pkg/sync/#WaitGroup) que é uma maneira conveniente de sincronizar processos concorrentes. > Um WaitGroup aguarda por uma coleção *goroutines* terminar seu processamento. A *goroutine* principal faz a chamada para o Add definir o número de *goroutines* -serão esperadas. Então, cada uma das *goroutines* rodam novamente e chamam Done -quando terminam sua execução. Ao mesmo tempo, Wait pode ser usada para bloquear até -que todas as *goroutines* tenham terminado. +que serão esperadas. Então, cada uma das *goroutines* roda novamente e chama +Done quando terminar sua execução. Ao mesmo tempo, Wait pode ser usada para +bloquear até que todas as *goroutines* tenham terminado. Ao esperar por `wg.Wait()` terminar sua execução antes de fazer nossas asserções, nós podemos ter certeza que todas as nossas *goroutines* tentaram `Inc` o `Contador`. @@ -175,7 +175,7 @@ O teste _provavelmente_ vai falhar com um número diferente, mas de toda forma ele demonstra que não roda corretamente quando várias *goroutines* tentam mudar o valor do contador ao mesmo tempo. -## Escreva código o suficiente para passar o teste +## Escreva código o suficiente para fazer o teste passar Uma solução simples é adicionar uma trava ao nosso `Contador`, um [`Mutex`](https://golang.org/pkg/sync/#Mutex) @@ -196,7 +196,7 @@ func (c *Contador) Inc() { ``` Isso significa que qualquer *goroutine* chamando `Inc` vai receber a trava em `Contador` -se eles forem o primeiro. Todas as outras *goroutines* vão ter que esperar por ele até +se for a primeira. Todas as outras *goroutines* vão ter que esperar por ele até que ele esteja `Unlock`, ou destravado, antes de ganhar o acesso. Agora se você rodar o teste novamente, ele deve funcionar porque cada uma das *goroutines* @@ -238,7 +238,7 @@ começar a chamar esses métodos diretamente. ![Demonstração de como um usuário dessa API pode chamar erroneamente o estado da trava](https://i.imgur.com/SWYNpwm.png) -_Isso parece como uma ideia muito ruim_ +_Isso parece uma péssima ideia_ ## Copiando mutexes @@ -283,7 +283,7 @@ Use essa função em seus teste quando for inicializar o `Contador`. Cobrimos algumas coisas no [pacote sync](https://golang.org/pkg/sync/): * `Mutex` que nos permite adicionar travas aos nossos dados -* `Waitgroup` que é uma maneira de esperar as *goroutines* terminar suas tarefas +* `Waitgroup` que é uma maneira de esperar as *goroutines* terminarem suas tarefas ### Quando usar travas em vez de *channels* e *goroutines*? @@ -293,7 +293,7 @@ que nos permite escrever código concorrente e seguro, então por que usar trava > Um erro comum de um iniciante em Go é usar demais os *channels* e *goroutines* apenas porque é possível e/ou porque é divertido. Não tenha medo de usar um `sync.Mutex` se -ele se encaixa melhor no seu problema. Go é pragmático em deixar você escolhar as +ele se encaixa melhor no seu problema. Go é pragmático em deixar você escolher as ferramentas que melhor resolvem o seu problema e não te forçar em um único estilo de código. From 05c0af3e956edf84de41e0ef04be9e8c80f87b35 Mon Sep 17 00:00:00 2001 From: Giovana Morais Date: Tue, 8 Oct 2019 20:35:35 -0300 Subject: [PATCH 09/16] =?UTF-8?q?corrigindo=20nome=20da=20fun=C3=A7=C3=A3o?= =?UTF-8?q?=20TestContador?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- primeiros-passos-com-go/sync.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/primeiros-passos-com-go/sync.md b/primeiros-passos-com-go/sync.md index ec5ac9fc..32cedafe 100644 --- a/primeiros-passos-com-go/sync.md +++ b/primeiros-passos-com-go/sync.md @@ -14,7 +14,7 @@ ele via teste e consertar essa falha. Queremos que nossa API nos dê um método para incrementar o contador e depois recuperar esse valor. ```go -func TesteContador(t *testing.T) { +func TestContador(t *testing.T) { t.Run("incrementar o contador 3 vezes o deixa com valor 3", func(t *testing.T) { contador := Contador{} contador.Inc() @@ -66,10 +66,10 @@ func (c *Contador) Valor() int { Agora tudo deve rodar e falhar ```text -=== RUN TesteContador -=== RUN TesteContador/incrementar_o_contador_3_vezes_o_deixa_com_valor_3 ---- FAIL: TesteContador (0.00s) - --- FAIL: TesteContador/incrementar_o_contador_3_vezes_o_deixa_com_valor_3 (0.00s) +=== RUN TestContador +=== RUN TestContador/incrementar_o_contador_3_vezes_o_deixa_com_valor_3 +--- FAIL: TestContador (0.00s) + --- FAIL: TestContador/incrementar_o_contador_3_vezes_o_deixa_com_valor_3 (0.00s) sync_test.go:27: recebido 0, desejado 3 ``` @@ -164,9 +164,9 @@ podemos ter certeza que todas as nossas *goroutines* tentaram `Inc` o `Contador` ## Tente rodar o teste ```text -=== RUN TesteContador/roda_concorrentemente_em_seguranca ---- FAIL: TesteContador (0.00s) - --- FAIL: TesteContador/roda_concorrentemente_em_seguranca (0.00s) +=== RUN TestContador/roda_concorrentemente_em_seguranca +--- FAIL: TestContador (0.00s) + --- FAIL: TestContador/roda_concorrentemente_em_seguranca (0.00s) sync_test.go:26: recebido 939, desejado 1000 FAIL ``` From e0d171446b6f8866f2dfb928877c717944fdc070 Mon Sep 17 00:00:00 2001 From: Giovana Morais Date: Tue, 8 Oct 2019 22:41:05 -0300 Subject: [PATCH 10/16] =?UTF-8?q?traduzindo=20coment=C3=A1rios=20faltantes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sync/v1/sync.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sync/v1/sync.go b/sync/v1/sync.go index 64e61fa7..8ec61c1c 100644 --- a/sync/v1/sync.go +++ b/sync/v1/sync.go @@ -1,16 +1,16 @@ package v1 -// Contador will increment a number +// Contador incrementa um número type Contador struct { valor int } -// Inc the count +// Incrementa o Contador func (c *Contador) Inc() { c.valor++ } -// Valor returns the current count +// Valor retorna o contador atual func (c *Contador) Valor() int { return c.valor } From 9d3e9337aedd0f206975c8f44e62d7f7e0aced52 Mon Sep 17 00:00:00 2001 From: Giovana Morais Date: Wed, 9 Oct 2019 20:24:58 -0300 Subject: [PATCH 11/16] trocando 'pra' pra 'para' --- primeiros-passos-com-go/sync.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/primeiros-passos-com-go/sync.md b/primeiros-passos-com-go/sync.md index 32cedafe..10a0d841 100644 --- a/primeiros-passos-com-go/sync.md +++ b/primeiros-passos-com-go/sync.md @@ -51,7 +51,7 @@ Tente de novo e ele falhará com o seguinte ./sync_test.go:18:13: contador.Valor undefined (type Contador has no field or method Valor) ``` -Então, pra finalmente fazer o teste rodar, podemos definir esses métodos +Então, para finalmente fazer o teste rodar, podemos definir esses métodos ```go func (c *Contador) Inc() { @@ -123,7 +123,7 @@ func assertContador(t *testing.T, recebido Contador, desejado int) { Isso foi muito fácil, mas agora nós temos uma requisição que é: ele precisa ser seguro o suficiente para usar em um ambiente com acesso concorrente. Vamos precisar -criar um teste pra exercitar isso. +criar um teste para exercitar isso. ## Escreva o teste primeiro @@ -147,7 +147,7 @@ t.Run("roda concorrentemente em segurança", func(t *testing.T) { }) ``` -Isso vai iterar pelo nosso `contadorDesejado` e disparar uma *goroutine* pra chamar `contador.Inc()`. +Isso vai iterar pelo nosso `contadorDesejado` e disparar uma *goroutine* para chamar `contador.Inc()`. Nós estamos usando [`sync.WaitGroup`](https://golang.org/pkg/sync/#WaitGroup) que é uma maneira conveniente de sincronizar processos concorrentes. @@ -300,7 +300,7 @@ de código. Paraphrasing: * **Use channels quando for passar a propridade de um dado** -* **Use mutexes pra gerenciar estados** +* **Use mutexes para gerenciar estados** ### go vet From 7a19fa8aeca3d1b583788bdd0aca78a6fc511b06 Mon Sep 17 00:00:00 2001 From: Giovana Morais Date: Mon, 28 Oct 2019 20:53:54 -0300 Subject: [PATCH 12/16] =?UTF-8?q?corrigindo=20algumas=20tradu=C3=A7=C3=B5e?= =?UTF-8?q?s=20de=20trechos=20esquecidos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- primeiros-passos-com-go/sync.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/primeiros-passos-com-go/sync.md b/primeiros-passos-com-go/sync.md index 10a0d841..654bfef7 100644 --- a/primeiros-passos-com-go/sync.md +++ b/primeiros-passos-com-go/sync.md @@ -97,7 +97,7 @@ func (c *Contador) Valor() int { ## Refatoração Não há muito o que refatorar, mas, dado que iremos escrever mais testes em -torno do `Contador`, vamos escrever uma pequena função de asserção `assertCount` +torno do `Contador`, vamos escrever uma pequena função de asserção `assertContador` para que o teste fique um pouco mais legível. @@ -297,7 +297,7 @@ ele se encaixa melhor no seu problema. Go é pragmático em deixar você escolhe ferramentas que melhor resolvem o seu problema e não te forçar em um único estilo de código. -Paraphrasing: +Parafraseando: * **Use channels quando for passar a propridade de um dado** * **Use mutexes para gerenciar estados** From 285c850a015728390fb61694cd316943cc0f5c47 Mon Sep 17 00:00:00 2001 From: Giovana Morais Date: Mon, 28 Oct 2019 20:57:08 -0300 Subject: [PATCH 13/16] arrumando typo --- primeiros-passos-com-go/sync.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/primeiros-passos-com-go/sync.md b/primeiros-passos-com-go/sync.md index 654bfef7..52caa3be 100644 --- a/primeiros-passos-com-go/sync.md +++ b/primeiros-passos-com-go/sync.md @@ -276,7 +276,7 @@ func NovoContador() *Contador { } ``` -Use essa função em seus teste quando for inicializar o `Contador`. +Use essa função em seus testes quando for inicializar o `Contador`. ## Resumindo From 9afdbe0ec669e393b406eda30ba28bc5fce1733f Mon Sep 17 00:00:00 2001 From: Giovana Morais Date: Thu, 14 Nov 2019 17:24:03 -0300 Subject: [PATCH 14/16] =?UTF-8?q?adicionando=20corre=C3=A7=C3=B5es=20feita?= =?UTF-8?q?s=20pela=20@larien?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 4 +- go.sum | 2 + primeiros-passos-com-go/sync.md | 116 ++++++++++++++++---------------- sync/v1/sync.go | 2 +- sync/v1/sync_test.go | 10 +-- sync/v2/sync.go | 2 +- sync/v2/sync_test.go | 14 ++-- 7 files changed, 78 insertions(+), 72 deletions(-) diff --git a/go.mod b/go.mod index bbdeb171..30b7c161 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module github.com/larien/learn-go-with-tests -require github.com/gorilla/websocket v1.4.0 +require github.com/gorilla/websocket v1.4.1 + +go 1.13 diff --git a/go.sum b/go.sum index cf4fbbaa..5f04ea28 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,4 @@ github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= diff --git a/primeiros-passos-com-go/sync.md b/primeiros-passos-com-go/sync.md index 52caa3be..ab1e0c1c 100644 --- a/primeiros-passos-com-go/sync.md +++ b/primeiros-passos-com-go/sync.md @@ -6,20 +6,20 @@ Queremos fazer um contador que é seguro para se usar concorrentemente. Vamos começar com um contador inseguro e verificar se seu comportamento funciona em um ambiente com apenas uma thread. -Depois vamos exercitar sua insegurança com múltiplas *goroutines* tentando usar -ele via teste e consertar essa falha. +Depois vamos testar sua falta de segurança com múltiplas *goroutines* tentando +usar o contador através de teste e consertar essa falha. ## Escreva o teste primeiro -Queremos que nossa API nos dê um método para incrementar o contador e depois recuperar esse valor. +Queremos que nossa API nos dê um método para incrementar o contador e depois recupere esse valor. ```go func TestContador(t *testing.T) { t.Run("incrementar o contador 3 vezes o deixa com valor 3", func(t *testing.T) { contador := Contador{} - contador.Inc() - contador.Inc() - contador.Inc() + contador.Incrementa() + contador.Incrementa() + contador.Incrementa() if contador.Valor() != 3 { t.Errorf("recebido %d, desejado %d", contador.Valor(), 3) @@ -44,17 +44,17 @@ type Contador struct { } ``` -Tente de novo e ele falhará com o seguinte +Tente de novo e ele falhará com o seguinte erro: ```text -./sync_test.go:14:10: contador.Inc undefined (type Contador has no field or method Inc) +./sync_test.go:14:10: contador.Incrementa undefined (type Contador has no field or method Incrementa) ./sync_test.go:18:13: contador.Valor undefined (type Contador has no field or method Valor) ``` -Então, para finalmente fazer o teste rodar, podemos definir esses métodos +Então, para finalmente fazer o teste rodar, podemos definir esses métodos: ```go -func (c *Contador) Inc() { +func (c *Contador) Incrementa() { } @@ -63,7 +63,7 @@ func (c *Contador) Valor() int { } ``` -Agora tudo deve rodar e falhar +Agora tudo deve rodar e falhar: ```text === RUN TestContador @@ -75,9 +75,9 @@ Agora tudo deve rodar e falhar ## Escreva código o suficiente para fazer o teste passar -Isso deve ser trivial para experts em Go como nós. Precisamos manter algum +Isso deve ser simples para _experts_ em Go como nós. Precisamos manter algum estado do contador no nosso datatype e daí incrementá-lo em cada chamada do -`Inc`. +`Incrementa`. ```go @@ -85,7 +85,7 @@ type Contador struct { valor int } -func (c *Contador) Inc() { +func (c *Contador) Incrementa() { c.valor++ } @@ -97,21 +97,21 @@ func (c *Contador) Valor() int { ## Refatoração Não há muito o que refatorar, mas, dado que iremos escrever mais testes em -torno do `Contador`, vamos escrever uma pequena função de asserção `assertContador` +torno do `Contador`, vamos escrever uma pequena função de asserção `verificaContador` para que o teste fique um pouco mais legível. ```go t.Run("incrementar o contador 3 vezes o deixa com valor 3", func(t *testing.T) { contador := Contador{} - contador.Inc() - contador.Inc() - contador.Inc() + contador.Incrementa() + contador.Incrementa() + contador.Incrementa() - assertContador(t, contador, 3) + verificaContador(t, contador, 3) }) -func assertContador(t *testing.T, recebido Contador, desejado int) { +func verificaContador(t *testing.T, recebido Contador, desejado int) { t.Helper() if recebido.Valor() != desejado { t.Errorf("recebido %d, quero receber %d", recebido.Valor(), desejado) @@ -121,14 +121,14 @@ func assertContador(t *testing.T, recebido Contador, desejado int) { ## Próximos passos -Isso foi muito fácil, mas agora nós temos uma requisição que é: ele precisa -ser seguro o suficiente para usar em um ambiente com acesso concorrente. Vamos precisar -criar um teste para exercitar isso. +Isso foi muito fácil, mas agora nós temos um requerimento que é: ele precisa +ser seguro o suficiente para ser usado em um ambiente com acesso concorrente. +Vamos precisar criar um teste para exercitar isso. ## Escreva o teste primeiro ```go -t.Run("roda concorrentemente em segurança", func(t *testing.T) { +t.Run("roda concorrentemente com segurança", func(t *testing.T) { contadorDesejado := 1000 contador := Contador{} @@ -137,29 +137,29 @@ t.Run("roda concorrentemente em segurança", func(t *testing.T) { for i:=0; i Um WaitGroup aguarda por uma coleção *goroutines* terminar seu processamento. +> Um WaitGroup aguarda por uma coleção de *goroutines* terminar seu processamento. A *goroutine* principal faz a chamada para o Add definir o número de *goroutines* que serão esperadas. Então, cada uma das *goroutines* roda novamente e chama Done quando terminar sua execução. Ao mesmo tempo, Wait pode ser usada para -bloquear até que todas as *goroutines* tenham terminado. +bloquear a execução até que todas as *goroutines* tenham terminado. Ao esperar por `wg.Wait()` terminar sua execução antes de fazer nossas asserções, nós -podemos ter certeza que todas as nossas *goroutines* tentaram `Inc` o `Contador`. +podemos ter certeza que todas as nossas *goroutines* tentaram `Incrementa` o `Contador`. ## Tente rodar o teste @@ -178,7 +178,7 @@ mudar o valor do contador ao mesmo tempo. ## Escreva código o suficiente para fazer o teste passar Uma solução simples é adicionar uma trava ao nosso `Contador`, um -[`Mutex`](https://golang.org/pkg/sync/#Mutex) +[`Mutex`](https://golang.org/pkg/sync/#Mutex). > Um Mutex é uma trava de exclusão mútua. O valor zero de um Mutex é um Mutex destravado. @@ -188,14 +188,14 @@ type Contador struct { valor int } -func (c *Contador) Inc() { +func (c *Contador) Incrementa() { c.mu.Lock() defer c.mu.Unlock() c.valor++ } ``` -Isso significa que qualquer *goroutine* chamando `Inc` vai receber a trava em `Contador` +Isso significa que qualquer *goroutine* chamando `Incrementa` vai receber a trava em `Contador` se for a primeira. Todas as outras *goroutines* vão ter que esperar por ele até que ele esteja `Unlock`, ou destravado, antes de ganhar o acesso. @@ -204,7 +204,7 @@ têm que esperar até que seja sua vez antes de fazer alguma mudança. ## Eu vi outros exemplos nos quais `sync.Mutex` está embutido dentro da struct. -Você pode ver exemplos como esse +Você pode ver exemplos como esse: ```go type Contador struct { @@ -213,26 +213,26 @@ type Contador struct { } ``` -É discutido que isso pode tornar o código um pouco mais elegante. +Há quem diga que isso pode tornar o código um pouco mais elegante. ```go -func (c *Contador) Inc() { +func (c *Contador) Incrementa() { c.Lock() defer c.Unlock() c.valor++ } ``` -Isso _parece_ legal, mas enquanto programação é uma disciplina altamente +Isso _parece_ legal, mas, apesar de programação ser uma área altamente subjetiva, isso é **feio e errado**. Às vezes as pessoas esquecem que tipos embutidos significam que os métodos daquele tipo se tornam _parte da interface pública_; e você geralmente não -quer isso. Não se esqueçam que devemos ser muito cuidadosos com as nossas APIs +quer isso. Não se esqueçam que devemos ter muito cuidado com as nossas APIs públicas. O momento que tornamos algo público é o momento que outros códigos podem acoplar-se a ele e nós queremos evitar acoplamentos desnecessários. -Expor `Lock` e `Unlock` é, no seu melhor caso, muito confuso e, no seu pior +Expôr `Lock` e `Unlock` é, no seu melhor caso, muito confuso e, no seu pior caso, potencialmente perigoso para o seu software se quem chamar o seu tipo começar a chamar esses métodos diretamente. @@ -247,27 +247,29 @@ Nossos testes passam, mas nosso código ainda é um pouco perigoso. Se você rodar `go vet` no seu código, deve receber um erro similar ao seguinte: ```text -sync/v2/sync_test.go:16: call of assertContador copies lock valor: v1.Contador contains sync.Mutex -sync/v2/sync_test.go:39: assertContador passes lock by valor: v1.Contador contains sync.Mutex +sync/v2/sync_test.go:16: call of verificaContador copies lock valor: v1.Contador contains sync.Mutex +sync/v2/sync_test.go:39: verificaContador passes lock by valor: v1.Contador contains sync.Mutex ``` Uma rápida olhada na documentação do [`sync.Mutex`](https://golang.org/pkg/sync/#Mutex) -nos diz o porquê +nos diz o porquê: > Um Mutex não deve ser copiado depois do primeiro uso. -Quando passamos nosso `Contador` \(por valor\) para `assertContador` ele vai tentar criar uma cópia do mutex. +Quando passamos nosso `Contador` \(por valor\) para `verificaContador`, +ele vai tentar criar uma cópia do mutex. -Para resolver isso, devemos passar um ponteiro para o nosso `Contador`. Vamos então mudar a assinatura de -`assertContador`. +Para resolver isso, devemos passar um ponteiro para o nosso `Contador`. +Vamos, então, mudar a assinatura de `verificaContador`. ```go -func assertContador(t *testing.T, recebido *Contador, desejado int) +func verificaContador(t *testing.T, recebido *Contador, desejado int) ``` -Nossos testes não vão mais compilar porque estamos tentando passar um `Contador` em vez de um `*Contador`. -Para resolver isso, é preferível criar um construtor que mostra aos usuários da nossa API que seria -melhor não inicializar o tipo ele mesmo. +Nossos testes não vão mais compilar porque estamos tentando passar um `Contador` +em vez de um `*Contador`. Para resolver isso, é preferível criar um construtor +que mostra aos usuários da nossa API que seria melhor não inicializar o tipo +ele mesmo. ```go @@ -280,26 +282,26 @@ Use essa função em seus testes quando for inicializar o `Contador`. ## Resumindo -Cobrimos algumas coisas no [pacote sync](https://golang.org/pkg/sync/): +Cobrimos algumas coisas do [pacote sync](https://golang.org/pkg/sync/): * `Mutex` que nos permite adicionar travas aos nossos dados * `Waitgroup` que é uma maneira de esperar as *goroutines* terminarem suas tarefas ### Quando usar travas em vez de *channels* e *goroutines*? -[Anteriormente cobrimos *goroutines* no primeiro capítulo de concorrência](concurrency.md) +[Anteriormente cobrimos *goroutines* no primeiro capítulo de concorrência](concorrencia.md) que nos permite escrever código concorrente e seguro, então por que usar travas? [A wiki do go tem uma página dedicada para esse tópico: Mutex ou Channel?](https://github.com/golang/go/wiki/MutexOrChannel) > Um erro comum de um iniciante em Go é usar demais os *channels* e *goroutines* apenas porque é possível e/ou porque é divertido. Não tenha medo de usar um `sync.Mutex` se -ele se encaixa melhor no seu problema. Go é pragmático em deixar você escolher as -ferramentas que melhor resolvem o seu problema e não te forçar em um único estilo +ele se encaixar melhor no seu problema. Go é pragmático em deixar você escolher as +ferramentas que melhor resolvem o seu problema e não te força em um único estilo de código. -Parafraseando: +Resumindo: -* **Use channels quando for passar a propridade de um dado** +* **Use channels quando for passar a propriedade de um dado** * **Use mutexes para gerenciar estados** ### go vet @@ -310,9 +312,9 @@ sutis no seu código antes que eles atinjam seus pobres usuários. ### Não use códigos embutidos apenas porque é conveniente * Pense a respeito do efeito que embutir códigos tem na sua API pública. -* Você _realmente_ quer expor esses métodos e ter pessoas acoplando o código +* Você _realmente_ quer expôr esses métodos e ter pessoas acoplando o código próprio delas a ele? -* No que diz respeito a mutexes, pode ser potencialmente um desastre de maneiras +* Em relação a mutexes, pode ser potencialmente um desastre de maneiras muito imprevisíveis e estranhas. Imagine algum código obscuro destravando um mutex quando não deveria; isso causaria erros muito estranhos e que seriam bastante difíceis de encontrar. diff --git a/sync/v1/sync.go b/sync/v1/sync.go index 8ec61c1c..f595b93b 100644 --- a/sync/v1/sync.go +++ b/sync/v1/sync.go @@ -6,7 +6,7 @@ type Contador struct { } // Incrementa o Contador -func (c *Contador) Inc() { +func (c *Contador) Incrementa() { c.valor++ } diff --git a/sync/v1/sync_test.go b/sync/v1/sync_test.go index 013d983a..0ddf1387 100644 --- a/sync/v1/sync_test.go +++ b/sync/v1/sync_test.go @@ -8,15 +8,15 @@ func TestContador(t *testing.T) { t.Run("incrementar o contador 3 vezes o deixa com valor 3", func(t *testing.T) { contador := Contador{} - contador.Inc() - contador.Inc() - contador.Inc() + contador.Incrementa() + contador.Incrementa() + contador.Incrementa() - assertContador(t, contador, 3) + verificaContador(t, contador, 3) }) } -func assertContador(t *testing.T, recebido Contador, desejado int) { +func verificaContador(t *testing.T, recebido Contador, desejado int) { t.Helper() if recebido.Valor() != desejado { t.Errorf("recebido %d, desejado %d", recebido.Valor(), desejado) diff --git a/sync/v2/sync.go b/sync/v2/sync.go index 832b5718..bd33578b 100644 --- a/sync/v2/sync.go +++ b/sync/v2/sync.go @@ -14,7 +14,7 @@ func NovoContador() *Contador { } // Incrementa o contador -func (c *Contador) Inc() { +func (c *Contador) Incrementa() { c.mu.Lock() defer c.mu.Unlock() c.valor++ diff --git a/sync/v2/sync_test.go b/sync/v2/sync_test.go index 57c61130..8b7be04f 100644 --- a/sync/v2/sync_test.go +++ b/sync/v2/sync_test.go @@ -9,11 +9,11 @@ func TestContador(t *testing.T) { t.Run("incrementar o contador 3 vezes o deixa com valor 3", func(t *testing.T) { contador := NovoContador() - contador.Inc() - contador.Inc() - contador.Inc() + contador.Incrementa() + contador.Incrementa() + contador.Incrementa() - assertContador(t, contador, 3) + verificaContador(t, contador, 3) }) t.Run("roda concorrentemente em seguranca", func(t *testing.T) { @@ -25,18 +25,18 @@ func TestContador(t *testing.T) { for i := 0; i < contadorDesejado; i++ { go func(w *sync.WaitGroup) { - contador.Inc() + contador.Incrementa() w.Done() }(&wg) } wg.Wait() - assertContador(t, contador, contadorDesejado) + verificaContador(t, contador, contadorDesejado) }) } -func assertContador(t *testing.T, recebido *Contador, desejado int) { +func verificaContador(t *testing.T, recebido *Contador, desejado int) { t.Helper() if recebido.Valor() != desejado { t.Errorf("recebido %d, desejado %d", recebido.Valor(), desejado) From 670ddab5167307f3b52416ea912a626e0f602f0c Mon Sep 17 00:00:00 2001 From: Lauren Maria Ferreira Date: Wed, 25 Dec 2019 18:38:51 -0300 Subject: [PATCH 15/16] =?UTF-8?q?Revis=C3=A3o=20de=20arquivos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sync/v1/sync.go | 4 ++-- sync/v1/sync_test.go | 7 +++---- sync/v2/sync.go | 4 ++-- sync/v2/sync_test.go | 18 ++++++++---------- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/sync/v1/sync.go b/sync/v1/sync.go index f595b93b..3eeb32b8 100644 --- a/sync/v1/sync.go +++ b/sync/v1/sync.go @@ -5,12 +5,12 @@ type Contador struct { valor int } -// Incrementa o Contador +// Incrementa o contador func (c *Contador) Incrementa() { c.valor++ } -// Valor retorna o contador atual +// Valor retorna a contagem atual func (c *Contador) Valor() int { return c.valor } diff --git a/sync/v1/sync_test.go b/sync/v1/sync_test.go index 0ddf1387..287cd900 100644 --- a/sync/v1/sync_test.go +++ b/sync/v1/sync_test.go @@ -5,7 +5,6 @@ import ( ) func TestContador(t *testing.T) { - t.Run("incrementar o contador 3 vezes o deixa com valor 3", func(t *testing.T) { contador := Contador{} contador.Incrementa() @@ -16,9 +15,9 @@ func TestContador(t *testing.T) { }) } -func verificaContador(t *testing.T, recebido Contador, desejado int) { +func verificaContador(t *testing.T, resultado Contador, esperado int) { t.Helper() - if recebido.Valor() != desejado { - t.Errorf("recebido %d, desejado %d", recebido.Valor(), desejado) + if resultado.Valor() != esperado { + t.Errorf("resultado %d, esperado %d", resultado.Valor(), esperado) } } diff --git a/sync/v2/sync.go b/sync/v2/sync.go index bd33578b..9c7236ab 100644 --- a/sync/v2/sync.go +++ b/sync/v2/sync.go @@ -2,7 +2,7 @@ package v1 import "sync" -// Contador vai incrementar um número +// Contador incrementa um número type Contador struct { mu sync.Mutex valor int @@ -20,7 +20,7 @@ func (c *Contador) Incrementa() { c.valor++ } -// Valor retorna o atual contador +// Valor retorna a contagem atual func (c *Contador) Valor() int { return c.valor } diff --git a/sync/v2/sync_test.go b/sync/v2/sync_test.go index 8b7be04f..5b69edda 100644 --- a/sync/v2/sync_test.go +++ b/sync/v2/sync_test.go @@ -6,7 +6,6 @@ import ( ) func TestContador(t *testing.T) { - t.Run("incrementar o contador 3 vezes o deixa com valor 3", func(t *testing.T) { contador := NovoContador() contador.Incrementa() @@ -16,14 +15,14 @@ func TestContador(t *testing.T) { verificaContador(t, contador, 3) }) - t.Run("roda concorrentemente em seguranca", func(t *testing.T) { - contadorDesejado := 1000 + t.Run("roda concorrentemente em segurança", func(t *testing.T) { + contagemEsperada := 1000 contador := NovoContador() var wg sync.WaitGroup - wg.Add(contadorDesejado) + wg.Add(contagemEsperada) - for i := 0; i < contadorDesejado; i++ { + for i := 0; i < contagemEsperada; i++ { go func(w *sync.WaitGroup) { contador.Incrementa() w.Done() @@ -31,14 +30,13 @@ func TestContador(t *testing.T) { } wg.Wait() - verificaContador(t, contador, contadorDesejado) + verificaContador(t, contador, contagemEsperada) }) - } -func verificaContador(t *testing.T, recebido *Contador, desejado int) { +func verificaContador(t *testing.T, resultado *Contador, esperado int) { t.Helper() - if recebido.Valor() != desejado { - t.Errorf("recebido %d, desejado %d", recebido.Valor(), desejado) + if resultado.Valor() != esperado { + t.Errorf("resultado %d, esperado %d", resultado.Valor(), esperado) } } From 7d754a40e602d2ec88a82168b917c7a9fd449a93 Mon Sep 17 00:00:00 2001 From: Lauren Maria Ferreira Date: Wed, 25 Dec 2019 19:07:20 -0300 Subject: [PATCH 16/16] =?UTF-8?q?Revis=C3=A3o=20do=20cap=C3=ADtulo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- primeiros-passos-com-go/sync.md | 183 +++++++++++++------------------- sync/v1/sync_test.go | 2 +- 2 files changed, 72 insertions(+), 113 deletions(-) diff --git a/primeiros-passos-com-go/sync.md b/primeiros-passos-com-go/sync.md index ab1e0c1c..6f57a4f6 100644 --- a/primeiros-passos-com-go/sync.md +++ b/primeiros-passos-com-go/sync.md @@ -2,12 +2,11 @@ [**Você pode encontrar todo o código para esse capítulo aqui**](https://github.com/larien/learn-go-with-tests/tree/master/sync) -Queremos fazer um contador que é seguro para se usar concorrentemente. +Queremos fazer um contador que é seguro para ser usado concorrentemente. -Vamos começar com um contador inseguro e verificar se seu comportamento funciona em um ambiente com apenas uma thread. +Vamos começar com um contador não seguro e verificar se seu comportamento funciona em um ambiente com apenas uma _thread_. -Depois vamos testar sua falta de segurança com múltiplas *goroutines* tentando -usar o contador através de teste e consertar essa falha. +Em seguida, vamos testar sua falta de segurança com várias *goroutines* tentando usar o contador dentro dos testes e consertar essa falha. ## Escreva o teste primeiro @@ -15,15 +14,15 @@ Queremos que nossa API nos dê um método para incrementar o contador e depois r ```go func TestContador(t *testing.T) { - t.Run("incrementar o contador 3 vezes o deixa com valor 3", func(t *testing.T) { + t.Run("incrementar o contador 3 vezes resulta no valor 3", func(t *testing.T) { contador := Contador{} contador.Incrementa() contador.Incrementa() contador.Incrementa() if contador.Valor() != 3 { - t.Errorf("recebido %d, desejado %d", contador.Valor(), 3) - } + t.Errorf("resultado %d, esperado %d", contador.Valor(), 3) + } }) } ``` @@ -34,7 +33,7 @@ func TestContador(t *testing.T) { ./sync_test.go:9:14: undefined: Contador ``` -## Escreva a quantidade mínima de código para o teste rodar e verifique a saída do teste que falhou +## Escreva o mínimo de código possível para fazer o teste rodar e verifique a saída do teste que tiver falhado Vamos definir `Contador`. @@ -44,7 +43,7 @@ type Contador struct { } ``` -Tente de novo e ele falhará com o seguinte erro: +Tente rodar o teste de novo e ele falhará com o seguinte erro: ```text ./sync_test.go:14:10: contador.Incrementa undefined (type Contador has no field or method Incrementa) @@ -67,17 +66,15 @@ Agora tudo deve rodar e falhar: ```text === RUN TestContador -=== RUN TestContador/incrementar_o_contador_3_vezes_o_deixa_com_valor_3 +=== RUN TestContador/incrementar_o_contador_3_vezes_resulta_no_valor_3 --- FAIL: TestContador (0.00s) - --- FAIL: TestContador/incrementar_o_contador_3_vezes_o_deixa_com_valor_3 (0.00s) - sync_test.go:27: recebido 0, desejado 3 + --- FAIL: TestContador/incrementar_o_contador_3_vezes_resulta_no_valor_3 (0.00s) + sync_test.go:27: resultado 0, esperado 3 ``` ## Escreva código o suficiente para fazer o teste passar -Isso deve ser simples para _experts_ em Go como nós. Precisamos manter algum -estado do contador no nosso datatype e daí incrementá-lo em cada chamada do -`Incrementa`. +Isso deve ser simples para _experts_ em Go como nós. Precisamos criar uma instância do tipo Contador e incrementá-lo com cada chamada de `Incrementa`. ```go @@ -96,70 +93,60 @@ func (c *Contador) Valor() int { ## Refatoração -Não há muito o que refatorar, mas, dado que iremos escrever mais testes em -torno do `Contador`, vamos escrever uma pequena função de asserção `verificaContador` -para que o teste fique um pouco mais legível. +Não há muito o que refatorar, mas já que iremos escrever mais testes em torno do `Contador`, vamos escrever uma pequena função de asserção `verificaContador` para que o teste fique um pouco mais legível. ```go -t.Run("incrementar o contador 3 vezes o deixa com valor 3", func(t *testing.T) { - contador := Contador{} - contador.Incrementa() - contador.Incrementa() - contador.Incrementa() +t.Run("incrementar o contador 3 vezes resulta no valor 3", func(t *testing.T) { + contador := Contador{} + contador.Incrementa() + contador.Incrementa() + contador.Incrementa() - verificaContador(t, contador, 3) + verificaContador(t, contador, 3) }) -func verificaContador(t *testing.T, recebido Contador, desejado int) { - t.Helper() - if recebido.Valor() != desejado { - t.Errorf("recebido %d, quero receber %d", recebido.Valor(), desejado) - } +func verificaContador(t *testing.T, resultado Contador, esperado int) { + t.Helper() + if resultado.Valor() != esperado { + t.Errorf("resultado %d, esperado %d", resultado.Valor(), esperado) + } } ``` ## Próximos passos -Isso foi muito fácil, mas agora nós temos um requerimento que é: ele precisa -ser seguro o suficiente para ser usado em um ambiente com acesso concorrente. -Vamos precisar criar um teste para exercitar isso. +Isso foi muito fácil, mas agora temos um requerimento que é: o programa precisa ser seguro o suficiente para ser usado em um ambiente com acesso concorrente. Vamos precisar criar um teste para exercitar isso. ## Escreva o teste primeiro ```go -t.Run("roda concorrentemente com segurança", func(t *testing.T) { - contadorDesejado := 1000 - contador := Contador{} - - var wg sync.WaitGroup - wg.Add(contadorDesejado) - - for i:=0; i Um WaitGroup aguarda por uma coleção de *goroutines* terminar seu processamento. -A *goroutine* principal faz a chamada para o Add definir o número de *goroutines* -que serão esperadas. Então, cada uma das *goroutines* roda novamente e chama -Done quando terminar sua execução. Ao mesmo tempo, Wait pode ser usada para -bloquear a execução até que todas as *goroutines* tenham terminado. +> Um WaitGroup aguarda por uma coleção de *goroutines* terminar seu processamento. A *goroutine* principal faz a chamada para o `Add` definir o número de *goroutines* que serão esperadas. Então, cada uma das *goroutines* é executada e chama `Done` quando termina sua execução. Ao mesmo tempo, `Wait` pode ser usado para bloquear a execução até que todas as *goroutines* tenham terminado. -Ao esperar por `wg.Wait()` terminar sua execução antes de fazer nossas asserções, nós -podemos ter certeza que todas as nossas *goroutines* tentaram `Incrementa` o `Contador`. +Ao esperar por `wg.Wait()` terminar sua execução antes de fazer nossas asserções, podemos ter certeza que todas as nossas *goroutines* tentaram chamar o `Incrementa` no `Contador`. ## Tente rodar o teste @@ -167,18 +154,15 @@ podemos ter certeza que todas as nossas *goroutines* tentaram `Incrementa` o `Co === RUN TestContador/roda_concorrentemente_em_seguranca --- FAIL: TestContador (0.00s) --- FAIL: TestContador/roda_concorrentemente_em_seguranca (0.00s) - sync_test.go:26: recebido 939, desejado 1000 + sync_test.go:26: resultado 939, esperado 1000 FAIL ``` -O teste _provavelmente_ vai falhar com um número diferente, mas de toda forma -ele demonstra que não roda corretamente quando várias *goroutines* tentam -mudar o valor do contador ao mesmo tempo. +O teste _provavelmente_ vai falhar com um número diferente, mas de qualquer forma demonstra que não roda corretamente quando várias *goroutines* tentam mudar o valor do contador ao mesmo tempo. ## Escreva código o suficiente para fazer o teste passar -Uma solução simples é adicionar uma trava ao nosso `Contador`, um -[`Mutex`](https://golang.org/pkg/sync/#Mutex). +Uma solução simples é adicionar uma trava ao nosso `Contador`, um [`Mutex`](https://golang.org/pkg/sync/#Mutex). > Um Mutex é uma trava de exclusão mútua. O valor zero de um Mutex é um Mutex destravado. @@ -195,14 +179,11 @@ func (c *Contador) Incrementa() { } ``` -Isso significa que qualquer *goroutine* chamando `Incrementa` vai receber a trava em `Contador` -se for a primeira. Todas as outras *goroutines* vão ter que esperar por ele até -que ele esteja `Unlock`, ou destravado, antes de ganhar o acesso. +Isso significa que qualquer *goroutine* chamando `Incrementa` vai receber a trava em `Contador` se for a primeira chamando essa função. Todas as outras *goroutines* vão ter que esperar por essa primeira execução até que ele esteja `Unlock`, ou destravado, antes de ganhar o acesso à instância de `Contador` alterada pela primeira chamada de função. -Agora se você rodar o teste novamente, ele deve funcionar porque cada uma das *goroutines* -têm que esperar até que seja sua vez antes de fazer alguma mudança. +Agora, se você rodar o teste novamente, ele deve funcionar porque cada uma das *goroutines* tem que esperar até que seja sua vez antes de fazer alguma mudança. -## Eu vi outros exemplos nos quais `sync.Mutex` está embutido dentro da struct. +## Já vi outros exemplos em que o `sync.Mutex` está embutido dentro da struct. Você pode ver exemplos como esse: @@ -213,7 +194,7 @@ type Contador struct { } ``` -Há quem diga que isso pode tornar o código um pouco mais elegante. +Há quem diga que isso torna o código um pouco mais elegante. ```go func (c *Contador) Incrementa() { @@ -223,22 +204,15 @@ func (c *Contador) Incrementa() { } ``` -Isso _parece_ legal, mas, apesar de programação ser uma área altamente -subjetiva, isso é **feio e errado**. +Isso _parece_ legal, mas, apesar de programação ser uma área altamente subjetiva, isso é **feio e errado**. -Às vezes as pessoas esquecem que tipos embutidos significam que os métodos -daquele tipo se tornam _parte da interface pública_; e você geralmente não -quer isso. Não se esqueçam que devemos ter muito cuidado com as nossas APIs -públicas. O momento que tornamos algo público é o momento que outros códigos -podem acoplar-se a ele e nós queremos evitar acoplamentos desnecessários. +Às vezes as pessoas esquecem que tipos embutidos significam que os métodos daquele tipo se tornam _parte da interface pública_; e você geralmente não quer isso. Não se esqueçam que devemos ter muito cuidado com as nossas APIs públicas. O momento que tornamos algo público é o momento que outros códigos podem acoplar-se a ele e queremos evitar acoplamentos desnecessários. -Expôr `Lock` e `Unlock` é, no seu melhor caso, muito confuso e, no seu pior -caso, potencialmente perigoso para o seu software se quem chamar o seu tipo -começar a chamar esses métodos diretamente. +Expôr `Lock` e `Unlock` é, no seu melhor caso, muito confuso e, no seu pior caso, potencialmente perigoso para o seu software se quem chamar o seu tipo começar a chamar esses métodos diretamente. ![Demonstração de como um usuário dessa API pode chamar erroneamente o estado da trava](https://i.imgur.com/SWYNpwm.png) -_Isso parece uma péssima ideia_ +_Isso parece uma péssima ideia._ ## Copiando mutexes @@ -251,25 +225,19 @@ sync/v2/sync_test.go:16: call of verificaContador copies lock valor: v1.Contador sync/v2/sync_test.go:39: verificaContador passes lock by valor: v1.Contador contains sync.Mutex ``` -Uma rápida olhada na documentação do [`sync.Mutex`](https://golang.org/pkg/sync/#Mutex) -nos diz o porquê: +Uma rápida olhada na documentação do [`sync.Mutex`](https://golang.org/pkg/sync/#Mutex) nos diz o porquê: > Um Mutex não deve ser copiado depois do primeiro uso. -Quando passamos nosso `Contador` \(por valor\) para `verificaContador`, -ele vai tentar criar uma cópia do mutex. +Quando passamos nosso `Contador` \(por valor\) para `verificaContador`, ele vai tentar criar uma cópia do mutex. -Para resolver isso, devemos passar um ponteiro para o nosso `Contador`. -Vamos, então, mudar a assinatura de `verificaContador`. +Para resolver isso, devemos passar um ponteiro para o nosso `Contador`. Vamos, então, mudar a assinatura de `verificaContador`. ```go -func verificaContador(t *testing.T, recebido *Contador, desejado int) +func verificaContador(t *testing.T, resultado *Contador, esperado int) ``` -Nossos testes não vão mais compilar porque estamos tentando passar um `Contador` -em vez de um `*Contador`. Para resolver isso, é preferível criar um construtor -que mostra aos usuários da nossa API que seria melhor não inicializar o tipo -ele mesmo. +Nossos testes não vão mais compilar porque estamos tentando passar um `Contador` ao invés de um `*Contador`. Para resolver isso, é melhor criar um construtor que mostra aos usuários da nossa API que seria melhor ele mesmo não inicializar seu tipo. ```go @@ -280,24 +248,20 @@ func NovoContador() *Contador { Use essa função em seus testes quando for inicializar o `Contador`. -## Resumindo +## Resumo -Cobrimos algumas coisas do [pacote sync](https://golang.org/pkg/sync/): +Falamos sobre algumas coisas do [pacote sync](https://golang.org/pkg/sync/): -* `Mutex` que nos permite adicionar travas aos nossos dados -* `Waitgroup` que é uma maneira de esperar as *goroutines* terminarem suas tarefas +* `Mutex` nos permite adicionar travas aos nossos dados +* `WaitGroup` é uma maneira de esperar as *goroutines* terminarem suas tarefas ### Quando usar travas em vez de *channels* e *goroutines*? -[Anteriormente cobrimos *goroutines* no primeiro capítulo de concorrência](concorrencia.md) +[Anteriormente falamos sobre *goroutines* no primeiro capítulo sobre concorrência](concorrencia.md) que nos permite escrever código concorrente e seguro, então por que usar travas? -[A wiki do go tem uma página dedicada para esse tópico: Mutex ou Channel?](https://github.com/golang/go/wiki/MutexOrChannel) +[A wiki do Go tem uma página dedicada para esse tópico: Mutex ou Channel?](https://github.com/golang/go/wiki/MutexOrChannel) -> Um erro comum de um iniciante em Go é usar demais os *channels* e *goroutines* apenas -porque é possível e/ou porque é divertido. Não tenha medo de usar um `sync.Mutex` se -ele se encaixar melhor no seu problema. Go é pragmático em deixar você escolher as -ferramentas que melhor resolvem o seu problema e não te força em um único estilo -de código. +> Um erro comum de um iniciante em Go é usar demais os *channels* e *goroutines* apenas porque é possível e/ou porque é divertido. Não tenha medo de usar um `sync.Mutex` se for uma solução melhor para o seu problema. Go é pragmático em deixar você escolher as ferramentas que melhor resolvem o seu problema e não te força em um único estilo de código. Resumindo: @@ -306,16 +270,11 @@ Resumindo: ### go vet -Não se esqueça de usar `go vet` nos seus scripts de build porque ele pode te alertar a respeito de bugs mais -sutis no seu código antes que eles atinjam seus pobres usuários. +Não se esqueça de usar `go vet` nos seus scripts de _build_ porque ele pode te alertar a respeito de bugs mais sutis no seu código antes que eles atinjam seus pobres usuários. ### Não use códigos embutidos apenas porque é conveniente * Pense a respeito do efeito que embutir códigos tem na sua API pública. -* Você _realmente_ quer expôr esses métodos e ter pessoas acoplando o código -próprio delas a ele? -* Em relação a mutexes, pode ser potencialmente um desastre de maneiras -muito imprevisíveis e estranhas. Imagine algum código obscuro destravando um -mutex quando não deveria; isso causaria erros muito estranhos e que seriam bastante -difíceis de encontrar. +* Você _realmente_ quer expôr esses métodos e ter pessoas acoplando o código próprio delas a ele? +* Mutexes podem se tornar um desastre de maneiras muito imprevisíveis e estranhas. Imagine um código inesperado destravando um mutex quando não deveria? Isso causaria erros muito estranhos que seriam muito difíceis de encontrar. diff --git a/sync/v1/sync_test.go b/sync/v1/sync_test.go index 287cd900..1d2d4cff 100644 --- a/sync/v1/sync_test.go +++ b/sync/v1/sync_test.go @@ -5,7 +5,7 @@ import ( ) func TestContador(t *testing.T) { - t.Run("incrementar o contador 3 vezes o deixa com valor 3", func(t *testing.T) { + t.Run("incrementar o contador 3 vezes resulta no valor 3", func(t *testing.T) { contador := Contador{} contador.Incrementa() contador.Incrementa()