Multithreading in GO

Hello.Help me please.
I have got MAP with values,and I need change one random value every 200 milliseconds.
The code is works, but when I release the program with the -race parameter, it is produces the following errors:
C:\gopath\src\Taxi>go run -race Taxi.go

WARNING: DATA RACE
Read at 0x00c042076960 by goroutine 8:
runtime.mapaccess1_fast64()
C:/Go/src/runtime/hashmap_fast.go:110 +0x0
main.cabbie()
C:/gopath/src/Taxi/Taxi.go:95 +0x15c
net/http.HandlerFunc.ServeHTTP()
C:/Go/src/net/http/server.go:1942 +0x58
github.com/gorilla/mux.(*Router).ServeHTTP()
C:/Go/src/github.com/gorilla/mux/mux.go:114 +0x18e
net/http.serverHandler.ServeHTTP()
C:/Go/src/net/http/server.go:2568 +0xc3
net/http.(*conn).serve()
C:/Go/src/net/http/server.go:1825 +0x721

Previous write at 0x00c042076960 by goroutine 6:
runtime.mapassign()
C:/Go/src/runtime/hashmap.go:485 +0x0
main.replacement()
C:/gopath/src/Taxi/Taxi.go:76 +0x1f9

Goroutine 8 (running) created at:
net/http.(*Server).Serve()
C:/Go/src/net/http/server.go:2668 +0x366
net/http.(*Server).ListenAndServe()
C:/Go/src/net/http/server.go:2585 +0xe7
net/http.ListenAndServe()
C:/Go/src/net/http/server.go:2787 +0xf5
main.main()
C:/gopath/src/Taxi/Taxi.go:87 +0x2ca

Goroutine 6 (running) created at:
main.main()
C:/gopath/src/Taxi/Taxi.go:83 +0x63

Program code:
//активные заявки
var request map[int]string = make(map[int]string)

//map с символами,для создания заявок
var symbol map[int]string = map[int]string{0: “a”, 1: “b”, 2: “c”, 3: “d”, 4: “e”,
5: “f”, 6: “j”, 7: “h”, 8: “i”, 9: “g”,
10: “k”, 11: “l”, 12: “m”, 13: “n”, 14: “o”,
15: “p”, 16: “q”, 17: “r”, 18: “s”, 19: “t”,
20: “u”, 21: “v”, 22: “w”, 23: “x”, 24: “y”,
25: “z”}
func replacement() { //функция замещения заявок раз в 200мс
for {
var number int = rand.Intn(sizeRequest)
firstPart = rand.Intn(sizeSymbol)
secondPart = rand.Intn(sizeSymbol)
muxLock.Lock()
var x string = symbol[firstPart] + symbol[secondPart]
request[number] = x
muxLock.Unlock()
time.Sleep(time.Millisecond * 200)
}
}
func mapFilling() { //функция начального заполнения мапа заявками
rand.Seed(time.Now().UTC().UnixNano())
for i := 0; i < sizeRequest; i++ {
firstPart = rand.Intn(sizeSymbol)
secondPart = rand.Intn(sizeSymbol)
muxLock.Lock()
request[i] = symbol[firstPart] + symbol[secondPart]
muxLock.Unlock()
}
}
func main() {
mapFilling()
go replacement()

}

The error message points to four lines of your code (Taxi.go): Lines 76, 83, 87, and 95. Look at these lines, most probably you’ll find that they access the same variable, and at least one of the lines belongs to a goroutine that your code creates.

I cannot comment on the code you posted because it does not seem to be the code that produced the error message. The error message contains references to main.cabbie(), net/http, and gorilla/mux, none of which appear in the code.

Tip: When posting code, you can keep it formatted by either indenting all the code by 4 spaces or surrounding your code by lines containing just three backticks:

```

I am sorry, but I don’t understand how to put indentation here (
Program code:
//активные заявки
var request map[int]string = make(map[int]string)

//map с символами,для создания заявок
var symbol map[int]string = map[int]string{0: “a”, 1: “b”, 2: “c”, 3: “d”, 4: “e”,
5: “f”, 6: “j”, 7: “h”, 8: “i”, 9: “g”,
10: “k”, 11: “l”, 12: “m”, 13: “n”, 14: “o”,
15: “p”, 16: “q”, 17: “r”, 18: “s”, 19: “t”,
20: “u”, 21: “v”, 22: “w”, 23: “x”, 24: “y”,
25: “z”}
func replacement() { //функция замещения заявок раз в 200мс
----for {
--------var number int = rand.Intn(sizeRequest)
--------firstPart = rand.Intn(sizeSymbol)
--------secondPart = rand.Intn(sizeSymbol)
--------muxLock.Lock()
--------var x string = symbol[firstPart] + symbol[secondPart]
--------request[number] = x
--------muxLock.Unlock()
--------time.Sleep(time.Millisecond * 200)
----}
}
func mapFilling() { //функция начального заполнения мапа заявками
----rand.Seed(time.Now().UTC().UnixNano())
----for i := 0; i < sizeRequest; i++ {
-------- firstPart = rand.Intn(sizeSymbol)
-------- secondPart = rand.Intn(sizeSymbol)
-------- muxLock.Lock()
-------- request[i] = symbol[firstPart] + symbol[secondPart]
-------- muxLock.Unlock()
----}
}
func main() {
----mapFilling()
----go replacement()

}
The problem in this line :
--------var x string = symbol[firstPart] + symbol[secondPart]
--------request[number] = x
I really appeal to the map from the herutin. But I need to do this. How can I refer to the map of the herutin in this case?

The problem still is that the error message does not match with the code.

According to the error message, the data race happens in a function named cabbie(), but neither does your code include a function named cabbie(), nor does main() call cabbie() anywhere.

The error message indicates that cabbie() and replacement() access the same variable:

Read ... by goroutine 8:
...
main.cabbie()
C:/gopath/src/Taxi/Taxi.go:95 ...

and

Previous write ... by goroutine 6:
...
main.replacement()
C:/gopath/src/Taxi/Taxi.go:76 ...

My guess is that the code inside cabbie() accesses the request map without locking the mutex.

Here is the func cabbie
func cabbie(w http.ResponseWriter, r *http.Request) { //ф-ция-обработчик запросов от таксистов
—vars := mux.Vars®
—interrogator := vars[“request”]
----if interrogator == “request” {
--------x := rand.Intn(sizeRequest)
--------fmt.Fprintln(w, “Заказ:”, request[x])
--------muxLock.Lock()
--------ArrRequest[numberAdminRequest] = application{title: request[x], views: ArrRequest[numberAdminRequest].views + 1}
--------numberAdminRequest++
--------muxLock.Unlock()
----}
}

All code:
package main

import (
“fmt”
“log”
“math/rand”
“net/http”
“sync”
“time”

"github.com/gorilla/mux"

)

var muxLock sync.Mutex

const sizeSymbol int = 25
const sizeRequest int = 50

//структура информации администратору
type application struct {
title string
views int
}

var numberAdminRequest int = 0
var firstPart, secondPart int

//информация по заявкам администратору
var ArrRequest map[int]application = make(map[int]application)

//активные заявки
var request map[int]string = make(map[int]string)

//map с символами,для создания заявок
var symbol map[int]string = map[int]string{0: “a”, 1: “b”, 2: “c”, 3: “d”, 4: “e”,
5: “f”, 6: “j”, 7: “h”, 8: “i”, 9: “g”,
10: “k”, 11: “l”, 12: “m”, 13: “n”, 14: “o”,
15: “p”, 16: “q”, 17: “r”, 18: “s”, 19: “t”,
20: “u”, 21: “v”, 22: “w”, 23: “x”, 24: “y”,
25: “z”}

func sort() { //подсчет количества выводов заявок таксисту
----for i := 0; i < len(ArrRequest); i++ {
-------for j := i + 1; j < len(ArrRequest); j++ {
------------if ArrRequest[i].title == ArrRequest[j].title {
---------------ArrRequest[j] = application{title: ArrRequest[i].title, views: ArrRequest[i].views + 1}
---------------ArrRequest[i] = application{views: 0}
------------}
--------}
----}
}

func mapFilling() { //функция начального заполнения мапа заявками
----rand.Seed(time.Now().UTC().UnixNano())
----for i := 0; i < sizeRequest; i++ {
-------firstPart = rand.Intn(sizeSymbol)
--------secondPart = rand.Intn(sizeSymbol)
--------muxLock.Lock()
--------request[i] = symbol[firstPart] + symbol[secondPart]
--------muxLock.Unlock()
----}
}

func replacement() { //функция замещения заявок раз в 200мс
----for {
--------var number int = rand.Intn(sizeRequest)
--------firstPart = rand.Intn(sizeSymbol)
--------secondPart = rand.Intn(sizeSymbol)
--------muxLock.Lock()
--------var x string = symbol[firstPart] + symbol[secondPart]
--------request[number] = x
--------muxLock.Unlock()
--------time.Sleep(time.Millisecond * 200)
----}
}
func main() {
----mapFilling()
----go replacement()
----router := mux.NewRouter()
----router.HandleFunc("/{request}", cabbie).Methods(“GET”)
----router.HandleFunc("/request/{admin}", admin).Methods(“GET”)
----log.Fatal(http.ListenAndServe(":3000", router))
}

func cabbie(w http.ResponseWriter, r *http.Request) { //ф-ция-обработчик запросов от таксистов
----vars := mux.Vars®
----interrogator := vars[“request”]
----if interrogator == “request” {
--------x := rand.Intn(sizeRequest)
--------fmt.Fprintln(w, “Заказ:”, request[x])
--------muxLock.Lock()
--------ArrRequest[numberAdminRequest] = application{title: request[x], views: ArrRequest[numberAdminRequest].views + 1}
--------numberAdminRequest++
--------muxLock.Unlock()
----}
}
func admin(w http.ResponseWriter, r *http.Request) { //функция-обработчик запросов от администратора
----vars := mux.Vars®
----interrogator := vars[“admin”]
----sort()
----if interrogator == “admin” {
--------for i := 0; i < len(ArrRequest); i++ {
--------if ArrRequest[i].views > 0 {
------------fmt.Fprintln(w, “Заказ:”, ArrRequest[i].title, “Количество показов:”, ArrRequest[i].views)
------------}
--------}
----}
}

formatted code:

package main

import (
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"sync"
	"time"

	"github.com/gorilla/mux"
)

var muxLock sync.Mutex

const sizeSymbol int = 25
const sizeRequest int = 50

//структура информации администратору
type application struct {
	title string
	views int
}

var numberAdminRequest int = 0
var firstPart, secondPart int

//информация по заявкам администратору
var ArrRequest map[int]application = make(map[int]application)

//активные заявки
var request map[int]string = make(map[int]string)

//map с символами,для создания заявок
var symbol map[int]string = map[int]string{0: "a", 1: "b", 2: "c", 3: "d", 4: "e",
	5: "f", 6: "j", 7: "h", 8: "i", 9: "g",
	10: "k", 11: "l", 12: "m", 13: "n", 14: "o",
	15: "p", 16: "q", 17: "r", 18: "s", 19: "t",
	20: "u", 21: "v", 22: "w", 23: "x", 24: "y",
	25: "z"}

func sort() { //подсчет количества выводов заявок таксисту
	for i := 0; i < len(ArrRequest); i++ {
		for j := i + 1; j < len(ArrRequest); j++ {
			if ArrRequest[i].title == ArrRequest[j].title {
				ArrRequest[j] = application{title: ArrRequest[i].title, views: ArrRequest[i].views + 1}
				ArrRequest[i] = application{views: 0}
			}
		}
	}
}

func mapFilling() { //функция начального заполнения мапа заявками
	rand.Seed(time.Now().UTC().UnixNano())
	for i := 0; i < sizeRequest; i++ {
		firstPart = rand.Intn(sizeSymbol)
		secondPart = rand.Intn(sizeSymbol)
		muxLock.Lock()
		request[i] = symbol[firstPart] + symbol[secondPart]
		muxLock.Unlock()
	}
}

func replacement() { //функция замещения заявок раз в 200мс
	for {
		var number int = rand.Intn(sizeRequest)
		firstPart = rand.Intn(sizeSymbol)
		secondPart = rand.Intn(sizeSymbol)
		muxLock.Lock()
		var x string = symbol[firstPart] + symbol[secondPart]
		request[number] = x
		muxLock.Unlock()
		time.Sleep(time.Millisecond * 200)
	}
}
func main() {
	mapFilling()
	go replacement()
	router := mux.NewRouter()
	router.HandleFunc("/{request}", cabbie).Methods("GET")
	router.HandleFunc("/request/{admin}", admin).Methods("GET")
	log.Fatal(http.ListenAndServe(":3000", router))
}

func cabbie(w http.ResponseWriter, r *http.Request) { //ф-ция-обработчик запросов от таксистов
	vars := mux.Vars(r)
	interrogator := vars["request"]
	if interrogator == "request" {
		x := rand.Intn(sizeRequest)
		fmt.Fprintln(w, "Заказ:", request[x])
		muxLock.Lock()
		ArrRequest[numberAdminRequest] = application{title: request[x], views: ArrRequest[numberAdminRequest].views + 1}
		numberAdminRequest++
		muxLock.Unlock()
	}
}
func admin(w http.ResponseWriter, r *http.Request) { //функция-обработчик запросов от администратора
	vars := mux.Vars(r)
	interrogator := vars["admin"]
	sort()
	if interrogator == "admin" {
		for i := 0; i < len(ArrRequest); i++ {
			if ArrRequest[i].views > 0 {
				fmt.Fprintln(w, "Заказ:", ArrRequest[i].title, "Количество показов:", ArrRequest[i].views)
			}
		}
	}
}

I was able to replicate the race by running:

go run -race main.go

(I called my source file main.go)

And then accessing http://127.0.0.1:3000/request from my browser.

The read referenced by the race detector in:

func cabbie(w http.ResponseWriter, r *http.Request) { //ф-ция-обработчик запросов от таксистов
	vars := mux.Vars(r)
	interrogator := vars["request"]
	if interrogator == "request" {
		x := rand.Intn(sizeRequest)
		fmt.Fprintln(w, "Заказ:", request[x])
		muxLock.Lock()
		ArrRequest[numberAdminRequest] = application{title: request[x], views: ArrRequest[numberAdminRequest].views + 1}
		numberAdminRequest++
		muxLock.Unlock()
	}
}

is the line:

fmt.Fprintln(w, "Заказ:", request[x])

The problem is the reference to request, a global variable.

This is a problem because this access happens outside the critical section that begins on the next line (muxLock.Lock()).

I moved the Fprintln to after the Lock. The race detector does not report that race anymore.

1 Like

If you need concurrent read/write access to a map, either wrap those calls in a sync.RWMutex - say “var requestMtx sync.RWMutex” - or, perhaps better, use golang.org/x/sync/syncmap package.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.