@blajlhimik

Как организовать код чтобы не возникала «concurrent map iteration and map write»?

код :
package main

import (
	"fmt"
	"net/http"
	"strconv"
	"time"
)

type Input struct {
	idmachine string
	iddevice  string
	messid    int
	lvl       int
}

type LastCommand struct {
	lvl       int
	idmachine string
}

type chinput chan Input

var arrInput = make(map[string]Input)
var lastInplvl = make(map[string]LastCommand)
var T = 0

func main() {
	arrChan := make(map[string]chinput)
	arrDevice := []string{"deviceid1", "deviceid2"}

	for _, value := range arrDevice {
		arrChan[value] = make(chinput)

		lastInplvl[value] = LastCommand{lvl: 0, idmachine: ""}

	}

	// handle worker
	for _, ch := range arrChan {
		go func(ch chinput) {
			for {
				/////////////////////////////////handler function input message///////////////
				input := <-ch
				fmt.Println("device worker :", input)
				/////////////////////////////////handler function input message///////////////
			}
		}(ch)
	}
	// handle worker

	//get worker
	for key, value := range arrChan {
		go GetWorker(key, value)
	}
	//get worker

	//http server
	http.HandleFunc("/", index)
	http.ListenAndServe(":8090", nil)
}

func GetWorker(iddevice string, workerChan chinput) {
	for {
		if len(arrInput) == 0 {
			time.Sleep(75 * time.Millisecond) // можно тут блокировать цикл пока длина <Arrinput> = 0, я как то работал с interruption в C для avr микроконтролеров в go нашел Signal но чтото я не понял ничего в нем
			continue
		}
		if lastInplvl[iddevice].lvl == 0 || lastInplvl[iddevice].lvl == 3 {
			var mesageChan = make(chan *Input, 1)
			timeout := time.After(10000 * time.Millisecond)
			go getMinimalMessageDeviceByComandLevel(iddevice, "", []int{0, 1}, mesageChan)
			select {
			case messGetCh := <-mesageChan:
				workerChan <- *messGetCh
			case <-timeout:
				///////////////////////////// put in arr resp error of timeout
				fmt.Println("timeout")
				///////////////////////////// put in arr resp error of timeout
			}
		} else if lastInplvl[iddevice].lvl == 1 || lastInplvl[iddevice].lvl == 2 {
			var mesageChan = make(chan *Input, 1)
			timeout := time.After(1500 * time.Millisecond)
			go getMinimalMessageDeviceByComandLevel(iddevice, lastInplvl[iddevice].idmachine, []int{2, 3}, mesageChan)
			select {
			case messGetCh := <-mesageChan:
				workerChan <- *messGetCh
			case <-timeout:
				///////////////////////////// put in arr resp error of timeout
				fmt.Println("timeout")
				lastInplvl[iddevice] = LastCommand{lvl: 0, idmachine: ""}
				//deletemesages
				///////////////////////////// put in arr resp error of timeout
			}
		} else {
			continue
		}
	}

}

func getMinimalMessageDeviceByComandLevel(iddevice string, idmachine string, findComandLevel []int, chanel chan *Input) {
	for {
		if len(arrInput) == 0 {
			continue
		}
		arrMess := arrInput
		var arrMessDevice = make(map[string]Input)
		for key, value := range arrMess {
			if value.iddevice == iddevice && ContainArrValue(findComandLevel, value.lvl) {
				if idmachine != "" {
					if idmachine == value.idmachine {
						arrMessDevice[key] = value
					}
				} else {
					arrMessDevice[key] = value
				}
			}
		}
		if len(arrMessDevice) == 0 {
			continue
		}
		minimalMesage := MinMax(arrMessDevice)
		message := arrMessDevice[minimalMesage]

		delete(arrInput, minimalMesage)

		chanel <- &message
		time.Sleep(time.Millisecond * 125)
	}
}

func ContainArrValue(arr []int, value int) bool {
	for _, val := range arr {
		if value == val {
			return true
		}
	}
	return false
}

func MinMax(array map[string]Input) string {
	var minVar Input
	var keyRet string
	for key, value := range array {
		minVar = value
		keyRet = key
		break
	}
	for key, value := range array {
		if minVar.messid > value.messid {
			minVar = value
			keyRet = key
		}
	}
	return keyRet
}

func AddInput(inputm *Input) {

	arrInput[inputm.iddevice+strconv.Itoa(inputm.messid)] = *inputm

}

func index(w http.ResponseWriter, req *http.Request) {
	a1 := Input{
		idmachine: "machine_qwk",
		iddevice:  "deviceid2",
		messid:    T,
		lvl:       0,
	}
	T++
	AddInput(&a1)
	a2 := Input{
		idmachine: "machine_qwk",
		iddevice:  "deviceid1",
		messid:    T,
		lvl:       0,
	}
	T++
	AddInput(&a2)
	a3 := Input{
		idmachine: "machine_qwk",
		iddevice:  "deviceid2",
		messid:    T,
		lvl:       0,
	}
	T++
	AddInput(&a3)
	a4 := Input{
		idmachine: "machine_qwk",
		iddevice:  "deviceid2",
		messid:    T,
		lvl:       0,
	}
	T++
	AddInput(&a4)
	a5 := Input{
		idmachine: "machine_qwk",
		iddevice:  "deviceid1",
		messid:    T,
		lvl:       0,
	}
	T++
	AddInput(&a5)
	a6 := Input{
		idmachine: "machine_qwk",
		iddevice:  "deviceid2",
		messid:    T,
		lvl:       0,
	}
	T++
	AddInput(&a6)

}


Можно как то избавится от time.Sleep() (особенно в 66 строке). Есть какой то "правельный код" для такой задачи, мне надо чтобы задачи для каждой Input.iddevice выполнялись паралельно, порядок выполнения: по возрастанию Input.messid, если послений выполненая команда имеет Input.lvl = 1 тогда делаются по тому жэ принципу задачи с Input.idmachine равен преведушему пока не будет Input.lvl = 3 или timeout.
Нашел вот такую фигню sync.RWMutex{}, попробовал залочить (и потом разолчить конечно же ) везде где есть чтение/запись/изменение map-ов (причем не только глобальных) и не помогло
  • Вопрос задан
  • 43 просмотра
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы