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
но почему должна?
Daniel
что ты сделал для того, чтобы тест не закончился до того, как все запущенные горутины отработают?
Slach
а как это сделать???
qutorz
Ну простейший вариант waitgroup. В твой код не вдавался, т.ч. не обязательно, что этот способ синхронизации каналов подойдет в твоем кейсе
Daniel
это не способ синхронизаци каналов, это смособ подсчета горутин
Slach
вот да, потому что Add(int)
а не Add(func)
Slach
qutorz
Ну, как я понимаю горутин "принимающие даные" много в его кейсе, а отправляющаю\ждущая обработки - одна. И как раз таки поставив wg.Wait можно дождаться завершения этих "обрабатывающих" горутин и продолжить работы
Slach
нет горутина принимающая данные ОДНА
Slach
но она не успевает прочитать из буферизированного канала
потому что функция бенчмарка, просто выходит раньше при b.N
и GC убивает и горутину и канал?
qutorz
Создай еще один канал, пиши в него в ф-ции, которая принимает данные, а в ф-ции где надо ждать читай из этого канала
qutorz
или тоже не подходит?
Slach
выглядит как окстыль...
мне так то надо забенчмаркать как много Collect смогут писать в одну ProcessRequest
а она в свою очередь должна писать в TSV файл
Slach
Alexander
да, именно так
Alexander
перед запуском рутины делаешь Add
Daniel
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
у приложения, очевидно, проблемы с архитектурой
Slach
а я не знал что можно делать range по каналу
какой интерфейс должна реализовывать сущность чтобы по ней можно было делать range ?
Alexander
по-моему только по slice, map, channel можно range делать
Anton
https://golang.org/ref/spec#RangeClause
Anton
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
неблокирующийся, как ни странно, иногда нужен
Daniel
более одного канала - это очень специфический случай
Anonymous
Oleg
Еще одно, из for d := range ch можно выйти только по закрытию канала из другой горутины
Anonymous
Ещё по break
Oleg
Ещё по break
Да, имел ввиду что нельзя выйти если сообщений в канале 0.
Daniel
Sergey
Daniel
нет, это нормально.
Daniel
просто это, действительно, специфика - отдельный канал под управление/таймер/whatever
Daniel
обычно остаточно закрытия канала для сигналинга
Slach
что-то я савсем запутался
вот сделал я канал буферизированный (размер буферв 100)
скажите, пока буффер не заполнится select в горутине читателе не вызовется??
так ведь?
Daniel
нет :)
Daniel
не так
Slach
а как?
Daniel
запись в буферизованный канал происходит мгновенно, пока в буфере есть место
Daniel
если место кончилось - блокируется до появления
Slach
я не про запись... я про чтение...
чтение когда вызывается?
Alexander
операция чтения блокируется во всех случаях когда пусто
Slach
а если не пусто??? то когда?
Alexander
операция записи когда буфера нет или он полон
Daniel
Alexander
то никогда. вопрос непонятен