Oleg
должен, defer сработает до выпонения любой из горутин, вызванных внутри тела функции
Daniel
Чет это совсем дебри :)
Slach
ок, понял спасибо
Slach
а как тогда сделать запись в канал асинхронную и при этом выйти из функции?? go func(req *web.Request) { g.Lock() defer g.Unlock() g.buffered_data <- *req // вот тут падает в panic }(req) заменил на просто g.buffered_data <- *req // вот тут падает в panic и оно подвисло на записи в канал с выходом из теста с exit code = 2
Daniel
Зачем нужна асинхронная запись в канал?
Slach
дак если просто в канал писать, то исходная функция которая в канал пишет подвисает? пока принимающая горутина из канала не получит? нет?
Daniel
Почитай про буферизованные каналы
Daniel
Короткий ответ - если буфер НЕ переполнен, не подвисает
Slach
ну я прочитал видимо ничего не понял, или понял как то не так ОК я сделал буфферизированый канал и убрал горутину с замыканием и оно теперь вообще бенчмарк не запускает а выходит с exit code=2 ;)
Slach
func NewGA2ClickHouse() GA2ClickHouse { return GA2ClickHouse{ schema_version: 1, form_k_exists_map: make(map[string]bool), db_field_list: make([]string, 0), db_field_map: make(map[string]int), db_to_form_map: make(map[string]string), nested_tables: make(map[string]NestedTable), buffered_data: make(chan web.Request, 100), } } func prepareCollectorToBenchmark() GA2ClickHouse { g := NewGA2ClickHouse() g.drop_database = true g.chunk_size = 100 g.data_dir = "./data" g.geoip_dir = "./geoip" g.database = "ga2clickhouse_test" g.cluster_name = "ga2clickhouse_test" g.table_prefix = "" g.clickhouse_hosts = []string{"local.ga2clickhouse.clickhouse.pro:8123"} g.CreateDataDir() g.InitGeoip() g.InitBrowseCap() g.InitClickHouseClient() g.CreateClickHouseTables() go g.ProcessRequest() return g } // дальше в бенчмарке func BenchmarkGA2ClickHouse_CollectSequenced(b *testing.B) { g := prepareCollectorToBenchmark() //defer g.ShutdownCollector() убрал чтобы не закрывать канал раньше log.Printf("b.N=%d", b.N) b.ResetTimer() for i := 0; i < b.N; i++ { req := NewTestRequest() rw := web.ResponseWriter(testResponseWriter{}) g.Collect(rw, &req) } }
Slach
блин у меня такое ощущение что у меня горутина которая данные принимает не успевает из канала прочитать к тому моменту когда бенчмарк функция выходит b.N=1
Slach
но по идее пока g.ProcessRequest не отработает g не должен быть убить через GC ??? такведь??
Daniel
но почему должна?
Daniel
что ты сделал для того, чтобы тест не закончился до того, как все запущенные горутины отработают?
Slach
а как это сделать???
qutorz
Ну простейший вариант waitgroup. В твой код не вдавался, т.ч. не обязательно, что этот способ синхронизации каналов подойдет в твоем кейсе
Daniel
это не способ синхронизаци каналов, это смособ подсчета горутин
Slach
вот да, потому что Add(int) а не Add(func)
qutorz
Ну, как я понимаю горутин "принимающие даные" много в его кейсе, а отправляющаю\ждущая обработки - одна. И как раз таки поставив wg.Wait можно дождаться завершения этих "обрабатывающих" горутин и продолжить работы
Slach
нет горутина принимающая данные ОДНА
Slach
но она не успевает прочитать из буферизированного канала потому что функция бенчмарка, просто выходит раньше при b.N и GC убивает и горутину и канал?
qutorz
Создай еще один канал, пиши в него в ф-ции, которая принимает данные, а в ф-ции где надо ждать читай из этого канала
qutorz
или тоже не подходит?
Slach
выглядит как окстыль... мне так то надо забенчмаркать как много Collect смогут писать в одну ProcessRequest а она в свою очередь должна писать в TSV файл
Slach
что ты сделал для того, чтобы тест не закончился до того, как все запущенные горутины отработают?
а как все таки правильно дождаться всех запущенные горутины? waitGroup ?
Alexander
да, именно так
Alexander
перед запуском рутины делаешь Add
Alexander
после тог, как она отработала Done. и в конце блока, где надо пождать пишешь Wait
Alexander
https://nathanleclaire.com/blog/2014/02/15/how-to-wait-for-all-goroutines-to-finish-executing-before-continuing/ - вот с примерами
Alexander
есть правда ощущение, что так нельзя писать - у него в последнем примере горутина, которая читает из jsonResponses в памяти осталась бы висеть, т.к. канал не закрыт
Alexander
но с WaitGroup код нормальный
Slach
спасибо за ссылку
Daniel
спасибо за ссылку
ссылка не поможе :(
Daniel
у приложения, очевидно, проблемы с архитектурой
Slach
а я не знал что можно делать range по каналу какой интерфейс должна реализовывать сущность чтобы по ней можно было делать range ?
Alexander
по-моему только по slice, map, channel можно range делать
Anton
https://golang.org/ref/spec#RangeClause
Slach
а как правильнее делать ? for { select } или
Slach
for i := range channel ?
🅁
смотря сколько у тебя каналов
🅁
которые ты хочешь слушать
🅁
я обысно использую 2 минимум
🅁
1 - данные, 2 управляющий
🅁
поэтому for {select case}
Slach
ну пока один в общем я понял что с точки зрения производительности это без разницы
Alexander
я бы посоветовал все-таки почитать книжку с примерами организации concurrency
qutorz
https://blog.golang.org/pipelines - Тут несколько неплохих примеров есть, чтоб далеко не ходить
Alexander
просто это основа языка по сути и лучше изучить основные приемы до написания кода - он как минимум будет более чистым с точки зрения языка
🅁
+ удобно иметь таймер ожидания, это еще один канал
Anonymous
for i := range channel можно просто закрыть канал
Daniel
а как правильнее делать ? for { select } или
select нужен в двух случаях 1. ты хочешь читать более одного канала 2. ты хочешь неблокирующегося ввода-вывода из/в канал(а)
Daniel
неблокирующийся, как ни странно, иногда нужен
Daniel
более одного канала - это очень специфический случай
Anonymous
более одного канала - это очень специфический случай
Обычно второй канал или управляющий или таймер
Oleg
Еще одно, из for d := range ch можно выйти только по закрытию канала из другой горутины
Anonymous
Ещё по break
Oleg
Ещё по break
Да, имел ввиду что нельзя выйти если сообщений в канале 0.
🅁
более одного канала - это очень специфический случай
почти всегда использую более 1 канала - данные, управление,таймер
Daniel
почти всегда использую более 1 канала - данные, управление,таймер
и, тем не менее, это очень специфический случай
Sergey
и, тем не менее, это очень специфический случай
использовать более одного канала это плохо? (
Daniel
нет, это нормально.
Daniel
просто это, действительно, специфика - отдельный канал под управление/таймер/whatever
Daniel
обычно остаточно закрытия канала для сигналинга
Slach
что-то я савсем запутался вот сделал я канал буферизированный (размер буферв 100) скажите, пока буффер не заполнится select в горутине читателе не вызовется?? так ведь?
Daniel
нет :)
Daniel
не так
Slach
а как?
Daniel
запись в буферизованный канал происходит мгновенно, пока в буфере есть место
Daniel
если место кончилось - блокируется до появления
Slach
я не про запись... я про чтение... чтение когда вызывается?
Alexander
операция чтения блокируется во всех случаях когда пусто
Slach
а если не пусто??? то когда?
Alexander
операция записи когда буфера нет или он полон
Daniel
операция чтения блокируется во всех случаях когда пусто
внутри селекта с секцией defaul - не блокируется
Alexander
то никогда. вопрос непонятен