Skip to content

Latest commit

 

History

History
90 lines (68 loc) · 3.74 KB

goroutines.md

File metadata and controls

90 lines (68 loc) · 3.74 KB

Горутины

В этой главе заставим наше приложение скидывать текущую статистику в консоль каждую секунду

Давайте сделаем так, чтобы приложение каждую секунду выводило в консоль значение возвращаемое ds.TotalHits(). Для этого добавим в файл main.go функцию printHits


Цикл for {} это аналог while (true) {} в PHP. Вообще в Go всего один цикл for


func printHits(ds *ds.Datastore) {
	for {
		time.Sleep(time.Second)
		fmt.Printf("Total visits %v\n", ds.TotalHits())
	}
}

Проблема в том, что нам нужно запустить данную функцию одновременно с http сервером. Для этого мы воспользуемся горутиной (goroutine). Горутина — это функция, выполняющаяся конкурентно с другими горутинами в том же адресном пространстве. Горутины легковесны, вы можете создавать их тысячами.

Для запуска горутины используется ключевое go. Изменим функцию main.go:

func main() {
	ds := ds.Datastore{}
	go printHits(&ds)
	e := echo.New()
	e.GET("/stat.js", handlers.Stat(&ds))
	e.GET("/counter.js", handlers.Counter(&ds))
	e.Start(":8080")
}

Важно что горутины выполняются конкурентно, а не параллельно. То есть не надо связывать выполнение горутины с потоком операционной системы. Горутины управляются не операционной системой, а самим Go (точнее планировщиком горутин).

Схема работы планировщика и горутин:

схема работы планировщика

Прежде чем идти дальше, давайте решим еще одну проблему которая присутствует в нашем приложении. Структура Datastore не предназначена для конкурентного использования. Что будет, если одна горутина вызовет RegisterHit, одновременно с другой? В этом случае мы получим ошибку выполнения программы!


Данные по умолчанию не предназначены для конкурентного использования!


Для того чтобы обеспечить синхронизацию данных между несколькими горутинами можно использоватью мьютексы.

Перепишем наш Datastore с использованием мьютекса из пакета sync:

package datastore

import (
	"sync"
)

type Datastore struct {
	hitCounter int
	mu         sync.RWMutex
}

func (ds *Datastore) RegisterHit() {
	ds.mu.Lock()
	defer ds.mu.Unlock()
	ds.hitCounter++
}

func (ds *Datastore) TotalHits() int {
	ds.mu.RLock()
	defer ds.mu.RUnlock()
	return ds.hitCounter
}

Через использование мьютекса мы обеспечиваем безопасный доступ к данным со стороны разных горутин. Однако испольование мьютекса не самый удобный способ, к счастью Go предлагает более удобный инструмент - каналы