Вопрос по каналам.

251 views
Skip to first unread message

Fyodor Ustinov

unread,
May 13, 2014, 3:24:29 PM5/13/14
to gola...@googlegroups.com
Hi!

А подскажите новичку - 

var ch chan bool
ch = make(chan bool)
...
ch = nil

При таком способе закрытия канала - сборщик мусора его вычистит рано или поздно?

Но, если честно, хочется посмотреть разработчикам языка в глаза и поинтерсоваться, чем они думали, когда не сделали атомарную операцию "послать в канал, и если он закрыт - вернуть ошибку".

WBR,
    Fyodor.

Oleg Bulatov

unread,
May 13, 2014, 3:46:04 PM5/13/14
to gola...@googlegroups.com
Привет.

Это нельзя называть закрытием канала, это не прервет цикл вида for i := range ch { ... } в соседней горутине.

Запись в канал сейчас примерно так и работает: "послать в канал, и если он закрыт - паниковать":
panic: runtime error: send on closed channel

Предполагается, что закрывать канал будет писатель. И что он как-то может отследить, что он не будет закрывать себе канал раньше времени.[1]

[1]: http://golang.org/pkg/builtin/#close It should be executed only by the sender, never the receiver

Artem Miolini

unread,
May 14, 2014, 8:14:46 AM5/14/14
to gola...@googlegroups.com
Да, при чтении канала из другой горутины, канал не закроется никогда и его ресурсы не будут очищены сборщиком мусора.


--
Вы получили это сообщение, поскольку подписаны на группу "Golang Russian".
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес golang-ru+...@googlegroups.com.
Чтобы настроить другие параметры, перейдите по ссылке https://groups.google.com/d/optout.

Dmitry Vyukov

unread,
May 14, 2014, 8:26:36 AM5/14/14
to gola...@googlegroups.com
2014-05-13 23:24 GMT+04:00 Fyodor Ustinov <ufm...@gmail.com>:
> Hi!
>
> А подскажите новичку -
>
> var ch chan bool
> ch = make(chan bool)
> ...
> ch = nil
>
> При таком способе закрытия канала - сборщик мусора его вычистит рано или
> поздно?

Канал будет вычищен если к нему не можут получить доступ ни одна горутина.
Соотв. если ch была последняя ссылка на этот канал, то после
присвоения nil, сборщик мусора её собирет.



> Но, если честно, хочется посмотреть разработчикам языка в глаза и
> поинтерсоваться, чем они думали, когда не сделали атомарную операцию
> "послать в канал, и если он закрыт - вернуть ошибку".

А в какой ситуации это может быть полезно?

Fyodor Ustinov

unread,
May 14, 2014, 8:45:05 AM5/14/14
to gola...@googlegroups.com
Я вот пытаюсь придумать "чистый" (т.е тот, который ни при каких условиях не вызовет панику) способ подписки одной горутины на услуги другой. И что-то у меня не получается.

Собственно тривиальная задача, которую я на эрланге реализую за 15 минут:
Есть поставщики информации.
Есть получатели информации.
Получатель подписывается на информацию у поставщика. У одного поставщика может быть несколько получателей. У получателя - несколько поставщиков.
Поставщик может в любой момент прекратить "вещание" (Планово или внепланово). Получатель может в любой момент прекратить "получение" (планово или внепланово)

Я вот или еще не грокнул глубинную логику голанга во всей полноте, или язык впринципе не предназначен для написания отказоустойчивых систем. Я понимаю, что скорее всего именно я дурак. Но как-то от этого не легче. :)

среда, 14 мая 2014 г., 15:26:36 UTC+3 пользователь Dmitry Vyukov написал:

Anton Ageev

unread,
May 14, 2014, 8:51:15 AM5/14/14
to gola...@googlegroups.com
2014-05-14 16:45 GMT+04:00 Fyodor Ustinov <ufm...@gmail.com>:
Я вот пытаюсь придумать "чистый" (т.е тот, который ни при каких условиях не вызовет панику) способ подписки одной горутины на услуги другой. И что-то у меня не получается.

Собственно тривиальная задача, которую я на эрланге реализую за 15 минут:
Есть поставщики информации.
Есть получатели информации.
Получатель подписывается на информацию у поставщика. У одного поставщика может быть несколько получателей. У получателя - несколько поставщиков.

Запрашиваем у поставщика канал для получения информации. Читаем из него.
 
Поставщик может в любой момент прекратить "вещание" (Планово или внепланово).

Поставщик закрывает канал и все получатели об этом узнают.
 
Получатель может в любой момент прекратить "получение" (планово или внепланово)

Получатель просто перестает читать канал.

Непонятно где тут поставщик должен проверять "закрытость" канала.
 
Я вот или еще не грокнул глубинную логику голанга во всей полноте, или язык впринципе не предназначен для написания отказоустойчивых систем. Я понимаю, что скорее всего именно я дурак. Но как-то от этого не легче. :)

среда, 14 мая 2014 г., 15:26:36 UTC+3 пользователь Dmitry Vyukov написал:

> Но, если честно, хочется посмотреть разработчикам языка в глаза и
> поинтерсоваться, чем они думали, когда не сделали атомарную операцию
> "послать в канал, и если он закрыт - вернуть ошибку".

А в какой ситуации это может быть полезно?

--
Вы получили это сообщение, поскольку подписаны на группу "Golang Russian".
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес golang-ru+...@googlegroups.com.
Чтобы настроить другие параметры, перейдите по ссылке https://groups.google.com/d/optout.



--
WBR, Anton

Fyodor Ustinov

unread,
May 14, 2014, 8:54:03 AM5/14/14
to gola...@googlegroups.com
Собственно тривиальная задача, которую я на эрланге реализую за 15 минут:
Есть поставщики информации.
Есть получатели информации.
Получатель подписывается на информацию у поставщика. У одного поставщика может быть несколько получателей. У получателя - несколько поставщиков.

Запрашиваем у поставщика канал для получения информации. Читаем из него.
Начнём с простого. Как запрашиваем?

WBR,
    Fyodor.

Anton Ageev

unread,
May 14, 2014, 8:55:44 AM5/14/14
to gola...@googlegroups.com
Да разные способы могут быть. Например, вызываем функцию или метод, которая вернёт канал.
 

WBR,
    Fyodor.

--
Вы получили это сообщение, поскольку подписаны на группу "Golang Russian".
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес golang-ru+...@googlegroups.com.
Чтобы настроить другие параметры, перейдите по ссылке https://groups.google.com/d/optout.



--
WBR, Anton

Fyodor Ustinov

unread,
May 14, 2014, 9:01:30 AM5/14/14
to gola...@googlegroups.com
Собственно тривиальная задача, которую я на эрланге реализую за 15 минут:
Есть поставщики информации.
Есть получатели информации.
Получатель подписывается на информацию у поставщика. У одного поставщика может быть несколько получателей. У получателя - несколько поставщиков.

Запрашиваем у поставщика канал для получения информации. Читаем из него.
Начнём с простого. Как запрашиваем?

Да разные способы могут быть. Например, вызываем функцию или метод, которая вернёт канал.
Сделайте скидку на то, что у меня бекграунд немного другой.

Вот есть у нас горутина. У неё как-то надо попросить канал для общения. Как?

Fyodor Ustinov

unread,
May 14, 2014, 9:23:06 AM5/14/14
to gola...@googlegroups.com
Запрашиваем у поставщика канал для получения информации. Читаем из него.
Начнём с простого. Как запрашиваем?

Да разные способы могут быть. Например, вызываем функцию или метод, которая вернёт канал.
 
Давайте я объясню как-бы я это сделал на эрланге, и Вы поймёте в чем у меня ступор.

Есть два процесса. А и Б. А - получатель данных, Б - поставщик.
А хочет подписаться на услуги Б.
1. А создаёт мониториг процесса Б. Если Б (еще/уже) не существует - А сразу получает сообщение "опачки". И в дальнейшем на любом шаге А получит такое сообщение, если с Б что-то случилось.
2.  А посылает Б сообщение "Подпиши меня, вот мой Pid" и ждёт ответа
3. Б создаёт мониторинг процесса А. С теми-же эффектами.
4. Б посылает сообщение процессу А - Ок, я тебя подписал.
5. Если кто-то из процессов в течение этого обмена сообщениями (или при дальнейшей работе) умрёт - то второй процесс об этом узнает и сможет принять соответствующие меры.
6. Ни в одном случае процессы не будут общаться с трупами (хотя для эрланга это и не страшно).

Вот как сделать что-то подобное на Go?

Anton Ageev

unread,
May 14, 2014, 9:39:57 AM5/14/14
to gola...@googlegroups.com
Про подписку вот вам пример: http://play.golang.org/p/s4lm1a6ONE

В Go нет процессов, как в Эрланге.
В Эрланге процесс - это горутина + канал. В Go каналы никак не связаны с горутинами.
Горутина может закончить свою работу, но канал будет работать дальше.
Один канал могут использовать много горутин.
Горутины не умирают просто так.
Паника в Go для серьезных ошибок, когда дальнейшая правильная работа программы невозможна.
В Go можно ловить панику и обрабатывать, но как по мне, использование паники в таком ключе скорее косяк в дизайне кода.

--
Вы получили это сообщение, поскольку подписаны на группу "Golang Russian".
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес golang-ru+...@googlegroups.com.
Чтобы настроить другие параметры, перейдите по ссылке https://groups.google.com/d/optout.



--
WBR, Anton

Fyodor Ustinov

unread,
May 14, 2014, 9:58:34 AM5/14/14
to gola...@googlegroups.com
Огромное спасибо за пример.

среда, 14 мая 2014 г., 16:39:57 UTC+3 пользователь Антон Агеев написал:

Fyodor Ustinov

unread,
May 14, 2014, 10:16:30 AM5/14/14
to gola...@googlegroups.com
Про подписку вот вам пример: http://play.golang.org/p/s4lm1a6ONE

Кстати, пример, того, что defer и gc не заменят деструктор, который вызывается автоматически по выходу из зоны видимости.
Если я правильно понимаю - в функции loop в блоке default селекта явно пропущен defer перед ds.RUnlock()
Т.е. golang решая одни стандартные "описки" (пропущенный break или второй оператор в if без {}) - принёс новые.

Dmitry Vyukov

unread,
May 14, 2014, 10:21:35 AM5/14/14
to gola...@googlegroups.com
Пожалуйста не надо пере-обобщать.

К тому же:

$ go run -race pubsub.go
Data source generate new value.
New consumer.
New consumer.
New consumer.
New consumer.
New consumer.
==================
WARNING: DATA RACE
Read by goroutine 5:
main.func·002()
pubsub.go:52 +0xcb
main.(*DataSource).loop()
pubsub.go:58 +0x151

Previous write by goroutine 10:
main.(*DataSource).Subscribe()
pubsub.go:27 +0x184
main.func·003()
pubsub.go:72 +0x5d

Goroutine 5 (running) created at:
main.NewDataSource()
pubsub.go:18 +0xcc
main.main()
pubsub.go:65 +0x42

Goroutine 10 (running) created at:
main.main()
pubsub.go:79 +0x12c
==================
==================
WARNING: DATA RACE
Read by goroutine 5:
main.func·002()
pubsub.go:52 +0x111
main.(*DataSource).loop()
pubsub.go:58 +0x151

Previous write by goroutine 10:
main.(*DataSource).Subscribe()
pubsub.go:27 +0x13f
main.func·003()
pubsub.go:72 +0x5d

Goroutine 5 (running) created at:
main.NewDataSource()
pubsub.go:18 +0xcc
main.main()
pubsub.go:65 +0x42

Goroutine 10 (running) created at:
main.main()
pubsub.go:79 +0x12c
==================
Consumer got new value: 5.
Consumer got new value: 5.
Consumer got new value: 5.
Consumer got new value: 5.
Data source generate new value.
Consumer got new value: 5.
Consumer got new value: 5.
Consumer got new value: 5.
Consumer got new value: 5.
Consumer got new value: 5.
Data source generate new value.
Consumer got new value: 5.
Consumer got new value: 5.
Consumer got new value: 5.
Consumer got new value: 5.
Consumer got new value: 5.
Data source generate new value.
Consumer got new value: 5.
Consumer got new value: 5.
Consumer got new value: 5.
Consumer got new value: 5.
Consumer got new value: 5.
Data source is stopped.
Consumer is stopped.
Consumer is stopped.
Consumer is stopped.
Consumer is stopped.
Consumer is stopped.
Found 2 data race(s)

Fyodor Ustinov

unread,
May 14, 2014, 10:32:34 AM5/14/14
to gola...@googlegroups.com
Hi!

Странно. У меня (с прописанным на место пропущенным defer) гонок нет. Без него - да. Проскакивают.

среда, 14 мая 2014 г., 17:21:35 UTC+3 пользователь Dmitry Vyukov написал:

Anton Ageev

unread,
May 14, 2014, 10:37:25 AM5/14/14
to gola...@googlegroups.com
Все верно, я забыл написать defer :)
И да, -race - мегаинструмент, позволяющий находить подобные ошибки.


--
Вы получили это сообщение, поскольку подписаны на группу "Golang Russian".
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес golang-ru+...@googlegroups.com.
Чтобы настроить другие параметры, перейдите по ссылке https://groups.google.com/d/optout.



--
WBR, Anton

Dmitry Vyukov

unread,
May 14, 2014, 10:37:54 AM5/14/14
to gola...@googlegroups.com
Что именно странно?

Dmitry Vyukov

unread,
May 14, 2014, 10:40:03 AM5/14/14
to gola...@googlegroups.com
2014-05-14 17:39 GMT+04:00 Anton Ageev <ant...@gmail.com>:
> 2014-05-14 17:23 GMT+04:00 Fyodor Ustinov <ufm...@gmail.com>:
>
>>>>> Запрашиваем у поставщика канал для получения информации. Читаем из
>>>>> него.
>>>>
>>>> Начнём с простого. Как запрашиваем?
>>>
>>>
>>> Да разные способы могут быть. Например, вызываем функцию или метод,
>>> которая вернёт канал.
>>>
>>
>> Давайте я объясню как-бы я это сделал на эрланге, и Вы поймёте в чем у
>> меня ступор.
>>
>> Есть два процесса. А и Б. А - получатель данных, Б - поставщик.
>> А хочет подписаться на услуги Б.
>> 1. А создаёт мониториг процесса Б. Если Б (еще/уже) не существует - А
>> сразу получает сообщение "опачки". И в дальнейшем на любом шаге А получит
>> такое сообщение, если с Б что-то случилось.
>> 2. А посылает Б сообщение "Подпиши меня, вот мой Pid" и ждёт ответа
>> 3. Б создаёт мониторинг процесса А. С теми-же эффектами.
>> 4. Б посылает сообщение процессу А - Ок, я тебя подписал.
>> 5. Если кто-то из процессов в течение этого обмена сообщениями (или при
>> дальнейшей работе) умрёт - то второй процесс об этом узнает и сможет принять
>> соответствующие меры.
>> 6. Ни в одном случае процессы не будут общаться с трупами (хотя для
>> эрланга это и не страшно).
>>
>> Вот как сделать что-то подобное на Go?
>
>
> Про подписку вот вам пример: http://play.golang.org/p/s4lm1a6ONE


Это решение не обрабатывает возможную "внезапную" отписку подписчика.

Я бы попробовал сделать так, что каждый подписчик имеет ровно один
входной канал и все производители шлют сообщения в него, и сообщение
содержит идентификатор топика, что бы различать производителей. Тогда
подписчику будет удобнее вычитывать из одного канала, нежели чем из
неизвестного числа каналов.

Anton Ageev

unread,
May 14, 2014, 10:45:31 AM5/14/14
to gola...@googlegroups.com


2014-05-14 18:40 GMT+04:00 'Dmitry Vyukov' via Golang Russian <gola...@googlegroups.com>:
> Про подписку вот вам пример: http://play.golang.org/p/s4lm1a6ONE
 
Это решение не обрабатывает возможную "внезапную" отписку подписчика.

Верно. Можно давать подписчику второй канал, в который он будет посылать уведомление об отписки.

Вариантов реализации - масса, на самом деле. При выборе реализации надо отталкиваться от условий конкретной задачи.

--
WBR, Anton

Fyodor Ustinov

unread,
May 14, 2014, 10:57:27 AM5/14/14
to gola...@googlegroups.com
Но согласитесь - была-бы возможность проверить закрытие канала и отправителем и получателем - было-бы сильно проще. Кому канал больше не нужен - тот его и закрыл. А второй - увидел что канал закрыт и что-то полезное в связи с этим сделал.

Т.е. для меня в этой ситуации непонятна сама логика поведения. Почему "неоткрытый" канал - это всего на всего lock при отсылке в него, который можно обойти, а "закрытый" канал - это паника, причем гарантированная, что-бы мы не делали?

среда, 14 мая 2014 г., 17:45:31 UTC+3 пользователь Антон Агеев написал:

Dmitry Vyukov

unread,
May 14, 2014, 11:08:23 AM5/14/14
to gola...@googlegroups.com
2014-05-14 18:45 GMT+04:00 Anton Ageev <ant...@gmail.com>:
>
>
>
> 2014-05-14 18:40 GMT+04:00 'Dmitry Vyukov' via Golang Russian
> <gola...@googlegroups.com>:
>
>> > Про подписку вот вам пример: http://play.golang.org/p/s4lm1a6ONE
>>
>>
>> Это решение не обрабатывает возможную "внезапную" отписку подписчика.
>
>
> Верно. Можно давать подписчику второй канал, в который он будет посылать
> уведомление об отписки.

Тут начнутся дедлоки из-за циклической зависимости.


> Вариантов реализации - масса, на самом деле. При выборе реализации надо
> отталкиваться от условий конкретной задачи.
>
> --
> WBR, Anton
>

Dmitry Vyukov

unread,
May 14, 2014, 11:12:39 AM5/14/14
to gola...@googlegroups.com
2014-05-14 18:57 GMT+04:00 Fyodor Ustinov <ufm...@gmail.com>:
> Но согласитесь - была-бы возможность проверить закрытие канала и
> отправителем и получателем - было-бы сильно проще. Кому канал больше не
> нужен - тот его и закрыл. А второй - увидел что канал закрыт и что-то
> полезное в связи с этим сделал.
>
> Т.е. для меня в этой ситуации непонятна сама логика поведения. Почему
> "неоткрытый" канал - это всего на всего lock при отсылке в него, который
> можно обойти, а "закрытый" канал - это паника, причем гарантированная,
> что-бы мы не делали?

По-моему тут проблема не в каналах, а в циклической зависимости.
Циклические зависимости обычно ни к чему хорошему не приводят. В
частности при зависимости через каналы ограниченного размера будут
возникать дедлоки, когда оба товарища пытаются слать друг другу в
полный канал.

Закрытие канала с обоих сторон плохо, т.к. обычно сообщения нельзя
просто так терять. А такая схема неминуемо приводит к тому, что
неизвестное сообщений будет теряться.



> среда, 14 мая 2014 г., 17:45:31 UTC+3 пользователь Антон Агеев написал:
>>
>>
>>
>>
>> 2014-05-14 18:40 GMT+04:00 'Dmitry Vyukov' via Golang Russian
>> <gola...@googlegroups.com>:
>>
>>> > Про подписку вот вам пример: http://play.golang.org/p/s4lm1a6ONE
>>>
>>>
>>> Это решение не обрабатывает возможную "внезапную" отписку подписчика.
>>
>>
>> Верно. Можно давать подписчику второй канал, в который он будет посылать
>> уведомление об отписки.
>>
>> Вариантов реализации - масса, на самом деле. При выборе реализации надо
>> отталкиваться от условий конкретной задачи.
>>
>> --
>> WBR, Anton
>

Dmitry Vyukov

unread,
May 14, 2014, 11:13:52 AM5/14/14
to gola...@googlegroups.com
Закрытие канала - это не более чем посылка сообщения "данные
закончились, новых сообщений больше не будет". Не надо пытаться
применять его для других целей.

Fyodor Ustinov

unread,
May 14, 2014, 12:03:19 PM5/14/14
to gola...@googlegroups.com


среда, 14 мая 2014 г., 18:13:52 UTC+3 пользователь Dmitry Vyukov написал:
Закрытие канала - это не более чем посылка сообщения "данные
закончились, новых сообщений больше не будет". Не надо пытаться
применять его для других целей.

Повторюсь - сейчас нет возможности сказать "не надо больше сообщений". По крайней мере простого способа без организации обратного канала. 
Ну и еще раз повторюсь - я не понимаю причину, по которой запись в неоткрытый канал это блокировка, а запись в закрытый - это паника. Я тайного смысла не вижу, и это меня пугает. :)

Dmitry Vyukov

unread,
May 14, 2014, 12:12:59 PM5/14/14
to gola...@googlegroups.com
Это неоднократно обсуждалось тут:
https://groups.google.com/forum/#!searchin/golang-nuts/send$20closed$20channel$20panic

Anton Ageev

unread,
May 14, 2014, 12:28:59 PM5/14/14
to gola...@googlegroups.com
2014-05-14 19:08 GMT+04:00 'Dmitry Vyukov' via Golang Russian <gola...@googlegroups.com>:
>> Это решение не обрабатывает возможную "внезапную" отписку подписчика.
>
>
> Верно. Можно давать подписчику второй канал, в который он будет посылать
> уведомление об отписки.

Тут начнутся дедлоки из-за циклической зависимости.

Не обязательно: http://play.golang.org/p/cW85xtA8aN


--
WBR, Anton

Dmitry Vyukov

unread,
May 15, 2014, 2:28:18 AM5/15/14
to gola...@googlegroups.com
Дедлоков я тут не вижу.

Но, что бы реализовать "У одного поставщика может быть несколько
получателей. У получателя - несколько поставщиков." придется и в
производителях и в потребителях генерировать динамические селекты с
помощью рефлект.
Reply all
Reply to author
Forward
0 new messages