Slach
wait нельзя вызывать до первого add, это в доках написано
хмм... дак он у меня wait вроде до первого add и не вызывается для копирования form сделал вот такой кастыль // WTF race condition for form?? func CopyFormData(form url.Values) (url.Values){ new_form := url.Values{} for k,v:= range form { new_v := make([]string,len(v)) copy(new_v, v) new_form[k] = new_v } return new_form } сразу вопрос для form в этой функции память же на стеке выделяется? может проще тогда сразу return сделать??? или это опять будет "копия ссылочного типа"? и я таки правильно делаю что копирую полностью все данные??
Daniel
копия - ок
Daniel
но
Daniel
кто, все же, в нее пишет, и зачем?
Slach
так после CopyFormData ушел RACE с конкурентной записью в map =) я понял что у меня код бенчмарка не правильный ;))) там единый экземпляр req.Form был и пишет в нее другая горутина бенчмарка который runParallel делает
Slava
почему кто-то вообще пишет в req.Form?
Slava
это данные для чтения только
Slach
ну с чего бы вдруг?? а метод Set там зачем? я ОБОГАЩАЮ данные всякими там geoip и user-agent парсингами 90% данных приходит в запросе 10% это обогащение теперь ГЛАВНЫЙ вопрос остался, каким образом wg.Wait может вызваться быстрее чем wg.Add ... вот это я уже реально не понимаю вот есть у меня код бенчмарка func BenchmarkGA2ClickHouse_CollectParallel(b *testing.B) { g := prepareCollectorToBenchmark() go g.ProcessRequest() //вот тут первой строкой идет g.wg.Add(1) defer g.ShutdownCollector() //вот тут идет close(g.bufferred_data) и потом g.wg.Wait() log.Printf("b.N=%d", b.N) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { rw := web.ResponseWriter(testResponseWriter{}) // вот тут for pb.Next() { req := NewTestRequest() g.Collect(rw, &req) //вот тут у меня пишется в канал g.buffered_data на который ProcessRequest читает } }) } я правильно понимаю что у меня shutdown collector из defer стартует раньше ProcessRequest потому что порожается куча горутин который в runParallel ???
Slava
потому что так нельзя делать
Slava
если вам пришли данные, то надо дополнительную структуру делать, если хотите на основе этих данных что-то дальше пропихивать
Slava
нельзя пришедшие данные изменять, это приводит к проблемам и неожиданным последствиям
Daniel
коллега
Daniel
Add надо делать ДО запуска горутины
Daniel
а то он делается асинхронно, и хер пойми когда
Slach
блин =((( ну что ж за фигня то такая ну вот я сделал вот так, вызываю Add до spawn ProcessRequest func (g *GA2ClickHouse) SpawnProcessRequest() { log.Print("SpawnProcessRequest BEGIN") g.wg.Add(1) log.Print("g.wg.Add(1)") go g.ProcessRequest() log.Print("SpawnProcessRequest END") } func (g *GA2ClickHouse) ProcessRequest() { log.Print("ProcessRequest BEGIN") defer func() { g.wg.Done() log.Print("ProcessRequest END defer g.wg.Done()") }() //... тут всякое } // over engineering detected ;) func (g *GA2ClickHouse) ShutdownCollector() { log.Println("ShutdownCollector BEGIN") close(g.buffered_data) g.wg.Wait() if g.tsv_file != nil && g.tsv_file.Fd() < math.MaxUint32 && g.writed_rows > 0 { g.pushDataToProcessDir() } if err:=g.geoip.Close(); err != nil { LogErrorAndExit(err) } log.Println("ShutdownCollector END") } =(( и блин даже дебагером не подцепиться =( оно вообще стало виснуть на g.wg.Wait() в логах выглядит вообще вот так 2017/02/05 15:43:54 SpawnProcessRequest BEGIN 2017/02/05 15:43:54 g.wg.Add(1) 2017/02/05 15:43:54 SpawnProcessRequest END 2017/02/05 15:43:54 b.N=1 2017/02/05 15:43:54 ShutdownCollector BEGIN 2017/02/05 15:43:54 ProcessRequest BEGIN 2017/02/05 15:43:54 ProcessRequest END after channel closed 2017/02/05 15:43:54 ProcessRequest END defer g.wg.Done()
Slach
race condition ушли но и работать перестало =(
engelbart
А посоветуйте, если кто какими линтерами пользуется для go кода? Развито ли это всякое?
Daniel
что значит - виснуть?
Slach
ну значит останавливаться и ничего не делать выглядит по логам так будто ShuwdownCollector застыл на g.wg.Wait() и ждет сцуко ;( но я точно знаю что я wg.Add(1) сделал только в одном месте... чего ему там еще ждать то
Daniel
вы, коллега, длаете ли где-нибудь Done()
Slach
2017/02/05 15:43:54 ProcessRequest END after channel closed 2017/02/05 15:43:54 ProcessRequest END defer g.wg.Done() да делаю и оно в логах показывает что делаю сразу после отрабатывания закрытия канала
Daniel
правильный паттерн вот такой: wg.Add(1) go func() { funcToBeRun() wg.Done() }()
engelbart
https://github.com/alecthomas/gometalinter
О спасибо, прямо сходу 10 мест нашел
Slach
почему я не могу внутри funcToBeRun() сделать defer wg.Done() ???? почему это не правильно?
Slach
ок... сейчас попробую
Slach
я понял спасибо
Daniel
можешь
Slach
так просто нагляднее
Daniel
просто мой способ идеоматический
Daniel
ага
Slach
нет не помогло... все равно ерунда какая то что именно ждет wait вообще не ясно 2017/02/05 16:17:11 SpawnProcessRequest BEGIN 2017/02/05 16:17:11 g.wg.Add(1) 2017/02/05 16:17:11 SpawnProcessRequest END 2017/02/05 16:17:11 b.N=1 2017/02/05 16:17:11 ProcessRequest BEGIN 2017/02/05 16:17:11 ShutdownCollector g.wg.Wait() BEGIN 2017/02/05 16:17:11 ProcessRequest END after channel closed 2017/02/05 16:17:11 SpawnProcessRequest END g.wg.Done()
Slava
выложи куда-то код, иначе мы никогда не угадаем
Slach
https://bitbucket.org/bloodjazman/ga2clickhouse/src/907c5b377accef93314e6cbd1882279e1091114a/?at=feature%2Fgo-collector
Slava
а можно поточнее?
Slach
вот бенчмарк который "подвисает" https://bitbucket.org/bloodjazman/ga2clickhouse/src/907c5b377accef93314e6cbd1882279e1091114a/src/ga2clickhouse/handlers_web_test.go?at=feature%2Fgo-collector&fileviewer=file-view-default#handlers_web_test.go-41 вот вызов wg.Wait() https://bitbucket.org/bloodjazman/ga2clickhouse/src/907c5b377accef93314e6cbd1882279e1091114a/src/ga2clickhouse/cli.go?at=feature%2Fgo-collector&fileviewer=file-view-default#cli.go-182 вот вызов wg.Add() и порождение функции читающей канал через select а также делающий wg.Done() по завершению https://bitbucket.org/bloodjazman/ga2clickhouse/src/907c5b377accef93314e6cbd1882279e1091114a/src/ga2clickhouse/data_processor.go?at=feature%2Fgo-collector&fileviewer=file-view-default#data_processor.go-14 канал буфферизованый
Anonymous
https://github.com/davelondon/jennifer/
Slach
а то что в sync.Waitgroup нет возможности прочитать counter это типа специально так задумано??
Daniel
ага
Daniel
но можно сочинить свой вэйтгруп на атомиках, и в нем сделать что пожелаешь
Slach
=(( да блин, как то хочется чтобы простые то вещи из коробки работали а тут мистика какая то... сделано все "по мануалу" (код с defer в котором wg.Done он в examples в официальной доке лежит) а оно не работает при этом суко... буквально с утра таже самая конструкция работала (не работало другое)
Daniel
нет там мистика
Slach
а что тогда есть?? через дебагер не подступишься логи говорят что .Done() вызван и что Add вызван 1 раз и вызван ДО Wait закоментарил вообще этот wg.Wait и бенчмарки заработали это как вообще ? ;((
Daniel
коллега
Daniel
вы написали херовый код
Daniel
вот и вся мистика
engelbart
https://github.com/davelondon/jennifer/
Зачем такое ? Есть реальный кейс ?
engelbart
Я не смог придумать зачем.
Sander
Всем привет, кто что думает, есть ли смысл читать эту книгу разработчику среднего уровня? https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882
Slach
Скажите я правильно понимаю что go func() {} исполняет горутину не сразу в момент вызова, а помещает горутину в FIFO очередь, а дальше если есть свободный Machine (максимум которых указан GOMAXPROCS), то запускает паралельно в момент порождения горутины? или все таки запуск накопленных горутин и переключение очереди осуществляется при syscall или при работе с каналами?
Oleg
Горутины запускает планировщик, там не FIFO.
Oleg
Нет гарантии что последовательно объявленные вызову горутин будут так же последовательно вызваны
Slach
ок. вызваны они будут сразу если есть свободное ядро?
Daniel
Когда-нибудь
Anton
добрый день! подскажите, возможно ли, и если да, то как, создать тип вроде type Points []Point и потом, создав переменную такого типа, иметь возможность заполнять этот массив через индексы, то есть: var test Points test[0] = ...
Daniel
Да
Daniel
А чего не попробовали-то?
Mikhail
ну тут же 344 человека, кто-нибудь да ответил бы, видимо
Anton
я попробовал
Daniel
И?
Anton
получаю ошибку index out of range
Anton
или как-то так
Daniel
А
Zver
Используй append
Daniel
То есть - принципиален отказ от append и make?
Zver
Либо сразу алоцируй все место.
Slach
=) append или make сделать надо сначала http://golang-book.ru/chapter-06-arrays-slices-maps.html https://github.com/golang/go/wiki/SliceTricks
Daniel
Тогда - надо map использовать
Zver
Но Мар будет заметно медленней.
Slach
ээээ... вам шашечки или ехать =) оно заметно медленее будет наверное на сотнях тысяч элементов =)
Daniel
Топикстартер не говорил, что надо быстро
Anton
у меня как раз сотни тысяч элементов =(
Zver
Ну тогда выделяйте мейком место и оперируйте точками. Никаких проблем в этом нет.
Zver
Как-то тестировал append, работает достаточно быстро. Почти как предварительное выделение и запись по индексу.
Anton
Ну тогда выделяйте мейком место и оперируйте точками. Никаких проблем в этом нет.
к сожалению, это мне не подходит у меня все сильно завязано на там, какой длины массив, а при make и изначально будет указанной длины