Skip to content

Commit

Permalink
Add channels concept
Browse files Browse the repository at this point in the history
  • Loading branch information
RezaSi committed Jul 14, 2022
1 parent 5dba26f commit 3f1be62
Show file tree
Hide file tree
Showing 14 changed files with 524 additions and 0 deletions.
5 changes: 5 additions & 0 deletions concepts/channels/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"blurb": "Channels are the pipes that connect concurrent goroutines.",
"authors": ["RezaSi"],
"contributors": ["RezaSi"]
}
113 changes: 113 additions & 0 deletions concepts/channels/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# About

Channels are a typed conduit through which you can send and receive values with the channel operator, <-. (The data flows in the direction of the arrow.)

```go
ch <- v // Send v to channel ch.
v := <-ch // Receive from ch, and assign value to v.
```

Create a new channel with make(chan val-type). Channels are typed by the values they convey.

```go
ch := make(chan int)
```

By default, sends and receives block wait until the other side is ready. This allows goroutines to synchronize without explicit locks or condition variables.


Example
```go
package main

import "fmt"

func exist(slice []int, x int, c chan bool) {
found := false
for _, v := range slice {
if v == x {
found = true
}
}
c <- found // send found to c
}

func main() {
slice := []int{7, 2, 8, -9, 4, 0}
findValue := 8

c := make(chan bool)
go exist(slice[:len(slice)/2], findValue, c)
go exist(slice[len(slice)/2:], findValue, c)
x, y := <-c, <-c // receive from c

fmt.Println(x || y)
}
```

## Buffered Channels

Channels can be buffered. Provide the buffer length as the second argument to make to initialize a buffered channel:

```go
ch := make(chan int, 100)
```

Sends to a buffered channel block only when the buffer is full. Receives block when the buffer is empty.


## Range and Close

A sender can close a channel to indicate that no more values will be sent. Receivers can test whether a channel has been closed by assigning a second parameter to the receive expression: after

```go
v, ok := <-ch
```

`ok` is `false` if there are no more values to receive and the channel is closed.

The loop `for i := range c` receives values from the channel repeatedly until it is closed.

### Notes:
- Only the sender should close a channel, never the receiver. Sending on a closed channel will cause a panic.

- Channels aren't like files; you don't usually need to close them. Closing is only necessary when the receiver must be told there are no more values coming, such as to terminate a range loop.

## Select

The `select` statement lets a goroutine wait on multiple communication operations.

A `select` blocks until one of its cases can run, then it executes that case. It chooses one at random if multiple are ready.

Example
```go
package main

import "fmt"

func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}

func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}

```
46 changes: 46 additions & 0 deletions concepts/channels/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Introduction

Channels are a typed conduit through which you can send and receive values with the channel operator, <-. (The data flows in the direction of the arrow.)

```go
ch <- v // Send v to channel ch.
v := <-ch // Receive from ch, and assign value to v.
```

Create a new channel with make(chan val-type). Channels are typed by the values they convey.

```go
ch := make(chan int)
```

By default, sends and receives block wait until the other side is ready. This allows goroutines to synchronize without explicit locks or condition variables.


Example
```go
package main

import "fmt"

func exist(slice []int, x int, c chan bool) {
found := false
for _, v := range slice {
if v == x {
found = true
}
}
c <- found // send found to c
}

func main() {
slice := []int{7, 2, 8, -9, 4, 0}
findValue := 8

c := make(chan bool)
go exist(slice[:len(slice)/2], findValue, c)
go exist(slice[len(slice)/2:], findValue, c)
x, y := <-c, <-c // receive from c

fmt.Println(x || y)
}
```
22 changes: 22 additions & 0 deletions concepts/channels/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[
{
"url": "https://go.dev/tour/concurrency/2",
"description": "Tour of Go: Channels"
},
{
"url": "https://go.dev/tour/concurrency/3",
"description": "Tour of Go: Buffered Channels"
},
{
"url": "https://go.dev/tour/concurrency/4",
"description": "Tour of Go: Range and Close"
},
{
"url": "https://go.dev/tour/concurrency/5",
"description": "Tour of Go: Select"
},
{
"url": "https://gobyexample.com/channels",
"description": "Go by Example: Channels"
}
]
1 change: 1 addition & 0 deletions exercises/concept/channels/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Hints
34 changes: 34 additions & 0 deletions exercises/concept/channels/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Instructions

In this exercise you'll be writing code to handle multilayer cache with golang channels.

## 1. Get value from fastest layer

We have multiple layers of cache, and we want to get the value from the fastest layer. the layers of cache given to you as `cacheLayers` parameter.

any cache struct in the cacheLayer is like this:

```go
type Cache struct {
ResponseTime time.Duration
Data map[string]string
}
```

as you see, the `ResponseTime` is the time it takes to get(or set) the value from(to) the cache, and the `Data` is the data stored in the cache.

you have to get a value from the layer have the value for this key as fast as possible and return it with `nil` error or return `ErrKeyNotExist` error if the key does not exist in any layer.


```go
GetValueFromFastestLayer(cacheLayers []Cache, key string) (string, error)
```

## 2. Set value to all layers
The key and value are given to you as `key` and `value` parameter.

you have to set the value to all `cacheLayers` as fast as possible.

```go
SetValueToAllLayers(cacheLayers []Cache, key string, value string) error
```
46 changes: 46 additions & 0 deletions exercises/concept/channels/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Introduction

Channels are a typed conduit through which you can send and receive values with the channel operator, <-. (The data flows in the direction of the arrow.)

```go
ch <- v // Send v to channel ch.
v := <-ch // Receive from ch, and assign value to v.
```

Create a new channel with make(chan val-type). Channels are typed by the values they convey.

```go
ch := make(chan int)
```

By default, sends and receives block wait until the other side is ready. This allows goroutines to synchronize without explicit locks or condition variables.


Example
```go
package main

import "fmt"

func exist(slice []int, x int, c chan bool) {
found := false
for _, v := range slice {
if v == x {
found = true
}
}
c <- found // send found to c
}

func main() {
slice := []int{7, 2, 8, -9, 4, 0}
findValue := 8

c := make(chan bool)
go exist(slice[:len(slice)/2], findValue, c)
go exist(slice[len(slice)/2:], findValue, c)
x, y := <-c, <-c // receive from c

fmt.Println(x || y)
}
```
16 changes: 16 additions & 0 deletions exercises/concept/channels/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"blurb": "Learn about numbers and type conversion by analyzing an assembly line in a car factory.",
"authors": ["RezaSi"],
"contributors": [],
"files": {
"solution": [
"channel.go"
],
"test": [
"channel_test.go"
],
"exemplar": [
".meta/exemplar.go"
]
}
}
23 changes: 23 additions & 0 deletions exercises/concept/channels/.meta/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Design

## Goal

The goal of this exercise is to teach the student how to deal with channels and select in Go.

## Learning objectives

- understand what channels are
- the difference between buffered and unbuffered channels
- closing channels
- for loop over a channel: for x := range ch { ... }
- use select to receive from several channels

## Concepts

- `channels`

## Prerequisites

- `functions`
- `for-loops`
- `range-iteration`
36 changes: 36 additions & 0 deletions exercises/concept/channels/.meta/exemplar.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package channels

func GetValueFromFastestLayer(cacheLayers []Cache, key string) (string, error) {
ch := make(chan string, len(cacheLayers))
for _, cache := range cacheLayers {
go func(cache Cache) {
value, _ := cache.Get(key)
ch <- value
}(cache)
}

for range cacheLayers {
value := <-ch
if value != "" {
return value, nil
}
}

return "", ErrKeyNotExist
}

func SetValueToAllLayers(cacheLayers []Cache, key string, value string) error {
ch := make(chan error, len(cacheLayers))
for _, cache := range cacheLayers {
go func(cache Cache) {
err := cache.Set(key, value)
ch <- err
}(cache)
}

for range cacheLayers {
<-ch
}

return nil
}
29 changes: 29 additions & 0 deletions exercises/concept/channels/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package channels

import (
"errors"
"time"
)

type Cache struct {
ResponseTime time.Duration
Data map[string]string
}

var ErrKeyNotExist = errors.New("key error")

func (c *Cache) Get(key string) (string, error) {
time.Sleep(c.ResponseTime)
if c.Data[key] != "" {
return c.Data[key], nil
} else {
return "", ErrKeyNotExist
}
}

func (c *Cache) Set(key string, value string) error {
time.Sleep(c.ResponseTime)
c.Data[key] = value

return nil
}
9 changes: 9 additions & 0 deletions exercises/concept/channels/channel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package channels

func GetValueFromFastestLayer(cacheLayers []Cache, key string) (string, error) {
panic("Please implement the GetValueFromFastestLayer function")
}

func SetValueToAllLayers(cacheLayers []Cache, key string, value string) error {
panic("Please implement the SetValueToAllLayers function")
}
Loading

0 comments on commit 3f1be62

Please sign in to comment.