VGrabko
@VGrabko
Golang, Php, Js

Почему мой код паникует неужели он не может спокойно работать?

Я всю ноч героически пытаюсь разобратся в синхронизации при помощи каналов а не мьютексов. На мьютексах у меня ужасный и не понятный код но он в секунду парсит около 3к url. Решил переписать его заодно разобравшись с каналами. Сейчас если в одной рутине запустить другую то где то через 200 рутин начинается паника.
var storageUrl map[string]int
var storageEmail map[string]int

func main() {
	storageUrl = make(map[string]int)
	storageEmail = make(map[string]int)
	queueLoadUrl := make(chan string)
	queueParseUrlHtml := make(chan string)
	queueParseEmailHtml := make(chan string)

	queueStorageUrl := make(chan string)
	queueStorageEmail := make(chan string)
	

	//defer func() {
	//	if r := recover(); r != nil {
	//		ReplacationFileSystem()
	//		os.Exit(1)
	//	}
	//}()


	go func() {
		for {
			msg := <-queueLoadUrl
			_, ok := storageUrl[msg]
			if !ok {
				queueStorageUrl <- msg
                               //если убрать здесь рутину то всё работает на ура но очень медленно
				go func() {
					queueParseUrlHtml <- parse.LoadUrl(msg)
				}()

			}
		}
	}()

	//эта рутина занята поиском других url в спарсенных документах
	go func() {
		for {
			msg := <-queueParseUrlHtml
			go func() {
				//передаём страницу на поиск емейлов.
				queueParseEmailHtml <- msg
				//парсим все ссылки и отдаём на загрузку.
				//что то вроде рекурсии
				for _, value := range parse.UrlHtml(msg) {
					queueLoadUrl <- value
				}
			}()
		}
	}()

	//эта рутина ищет email в спарсенных url
	go func() {
		for {
			msg := <-queueParseEmailHtml
			go func() {
				for _, value := range parse.EmailHtml(msg) {
					queueStorageEmail <- value
				}
			}()
		}
	}()

	//эта рутина обновляет данные в Storage
	go func() {
		for {
			select {
			case msg1 := <-queueStorageUrl:
				storageUrl[msg1] = 0
			case msg2 := <-queueStorageEmail:
				storageEmail[msg2] = 0
			}
		}
	}()

	//рутина занята репликацией.

	go func() {
		for {
			time.Sleep(5000 * time.Millisecond)
			ReplacationFileSystem()
		}
	}()

	//получает команду
	for {
		reader := bufio.NewReader(os.Stdin)
		fmt.Print("Фраза или url: ")
		text, _ := reader.ReadString('\n')
		fmt.Println("Начал парсинг")
		queueLoadUrl <- text
	}
}

func ReplacationFileSystem() {
	var buf bytes.Buffer
	for key, _ := range storageEmail {
		buf.Write([]byte(key + "\n"))
	}
	file, _ := os.Create("email.txt")
	defer file.Close()
	file.Write(buf.Bytes())
}


Ошибка с описанием в коде више. И чуть не забыл пакет parse
var Host string

func LoadUrl(urls string) string {
	u, err := url.Parse(strings.TrimSpace(urls))
	if err != nil {
		return ""
	}
	rw := &sync.RWMutex{}
	if u.Host == "" {
		rw.RLock()
		u.Host = Host
		rw.RUnlock()
	} else {
		rw.Lock()
		Host = u.Host
		rw.Unlock()
	}

	if u.Scheme == "" {
		u.Scheme = "http"
	}

	res, err := http.Get(u.String())
	if err != nil {
		return ""
	}
	d, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		return ""
	}
	return string(d)
}

func UrlHtml(s string) map[int]string {
	urls := map[int]string{}
	count := 0

	doc, err := html.Parse(strings.NewReader(s))
	if err != nil {
		log.Fatal(err)
	}
	var f func(*html.Node)
	f = func(n *html.Node) {
		if n.Type == html.ElementNode && n.Data == "a" {
			for _, a := range n.Attr {
				if a.Key == "href" {
					urls[count] = a.Val
					count++
					break
				}
			}
		}
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			f(c)
		}
	}
	f(doc)

	return urls
}

func EmailHtml(str string) []string {
	r := regexp.MustCompile("([a-z0-9_\\.\\-]+)\\@(([a-z0-9\\-])+\\.)+([a-z]{2,6})")
	return r.FindAllString(str, -1)
}


goroutine 26385 [GC assist wait]:
net/url.(*URL).String(0x1648e2c0, 0x0, 0x0)
        C:/Go/src/net/url/url.go:663 +0x2f
parser/parse.LoadUrl(0x19345270, 0x43, 0x0, 0x0)
        C:/Documents and Settings/lucifer/Рабочий стол/go/src/parser/parse/href.
go:37 +0x10e
main.main.func1.1(0x10a164c0, 0x19345270, 0x43)
        C:/Documents and Settings/lucifer/Рабочий стол/go/src/parser/main.go:45
+0x29
created by main.main.func1
        C:/Documents and Settings/lucifer/Рабочий стол/go/src/parser/main.go:46
+0xd8

goroutine 26386 [GC assist wait]:
net/url.(*URL).String(0x1648e300, 0x0, 0x0)
        C:/Go/src/net/url/url.go:663 +0x2f
parser/parse.LoadUrl(0x19345400, 0x4e, 0x0, 0x0)
        C:/Documents and Settings/lucifer/Рабочий стол/go/src/parser/parse/href.
go:37 +0x10e
main.main.func1.1(0x10a164c0, 0x19345400, 0x4e)
        C:/Documents and Settings/lucifer/Рабочий стол/go/src/parser/main.go:45
+0x29
created by main.main.func1
        C:/Documents and Settings/lucifer/Рабочий стол/go/src/parser/main.go:46
+0xd8

goroutine 26387 [runnable]:
main.main.func1.1(0x10a164c0, 0x15176540, 0x31)
        C:/Documents and Settings/lucifer/Рабочий стол/go/src/parser/main.go:44
created by main.main.func1
        C:/Documents and Settings/lucifer/Рабочий стол/go/src/parser/main.go:46
+0xd8

goroutine 26388 [runnable]:
main.main.func1.1(0x10a164c0, 0x19345450, 0x50)
        C:/Documents and Settings/lucifer/Рабочий стол/go/src/parser/main.go:44
created by main.main.func1
        C:/Documents and Settings/lucifer/Рабочий стол/go/src/parser/main.go:46
+0xd8
  • Вопрос задан
  • 223 просмотра
Решения вопроса 2
VGrabko
@VGrabko Автор вопроса
Golang, Php, Js
благодарю FireGM за ответ раниее в котором сказано что надо руками контролировать количество рутин. Ох бы я прочитал это вчера вечером...
Ответ написан
Комментировать
@ivahaev
Программист, связист и просто хороший человек
Здесь явное злоупотребление каналами. Я бы даже сказал, что не правильное их использование. Если получая из канала сообщение, тут же запускаешь горутину, то какой смысл в канале? Можно сильно упростить, убрав канал и переделав на вызов функции.
Парсинг сделать бы вот с этой штукой WaitGroup. Собрать все ссылки и только потом писать в мапы. И вот тут-то и нужен локер для конкретной мапы, ну или так же через канал можно реализовать.

Что касается паники в текущей версии, похоже, что происходит в момент считывания мапы в функции ReplacationFileSystem. Никакого локера там нет. В это же время другая рутина может писать в эту же мапу.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

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

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