Подтвердить что ты не робот

Можно ли одновременно ждать в обоих каналах и дескрипторах файлов в Go?

Я знаю, что могу ждать по нескольким каналам, используя синтаксис select {} в Go, и ждать нескольких дескрипторов файлов, используя syscall.Select() или подобные функции. Но можно ли одновременно ждать по обеим каналам?

Для фона я хочу иметь goroutine, который принимает сообщения по каналу и пересылает их по соединению сокета (предоставляется gozmq), одновременно ожидая ответа на соединение сокета.

Из-за требований безопасности потока в базовой библиотеке сокет может быть доступен только в одном потоке за раз, поэтому мне было интересно, есть ли способ справиться с этим из одного goroutine.

4b9b3361

Ответ 1

Выбор как для канала, так и для файлового дескриптора невозможен, поскольку абстракции находятся на разных уровнях. Каналы обрабатываются run runtime и файловыми дескрипторами операционной системы. Вам нужно сделать мост между ними, и это можно сделать с помощью net.Pipe().

Довольно многое, что вам нужно сделать, - выделить один goroutine на epoll()/select(), чтобы посмотреть ваши zmq-сокеты и один "просыпаться" net.Pipe(). Это ваш сервер опроса. Другой горутин слушает ваши каналы чтения и записи. Когда кто-то отправляет по каналам чтения или записи, второй goroutine отправил бы на трубку, чтобы разбудить сервер опроса.

Так работает сетевой пакет в стандартной библиотеке. Я настоятельно рекомендую прочитать его для вдохновения (или воровства... лицензия BSD очень либеральная).

Вот описание pollServer из самой сети. Возможно, вам придется прочитать код, чтобы понять, что это говорит, но этот раздел из fd.go должен быть хорошим местом для начала поиска.

// A pollServer helps FDs determine when to retry a non-blocking
// read or write after they get EAGAIN.  When an FD needs to wait,
// send the fd on s.cr (for a read) or s.cw (for a write) to pass the
// request to the poll server.  Then receive on fd.cr/fd.cw.
// When the pollServer finds that i/o on FD should be possible
// again, it will send fd on fd.cr/fd.cw to wake any waiting processes.
// This protocol is implemented as s.WaitRead() and s.WaitWrite().
//
// There is one subtlety: when sending on s.cr/s.cw, the
// poll server is probably in a system call, waiting for an fd
// to become ready.  It not looking at the request channels.
// To resolve this, the poll server waits not just on the FDs it has
// been given but also its own pipe.  After sending on the
// buffered channel s.cr/s.cw, WaitRead/WaitWrite writes a
// byte to the pipe, causing the pollServer poll system call to
// return.  In response to the pipe being readable, the pollServer
// re-polls its request channels.
//
// Note that the ordering is "send request" and then "wake up server".
// If the operations were reversed, there would be a race: the poll
// server might wake up and look at the request channel, see that it
// was empty, and go back to sleep, all before the requester managed
// to send the request.  Because the send must complete before the wakeup,
// the request channel must be buffered.  A buffer of size 1 is sufficient
// for any request load.  If many processes are trying to submit requests,
// one will succeed, the pollServer will read the request, and then the
// channel will be empty for the next process request.  A larger buffer
// might help batch requests.
//
// To avoid races in closing, all fd operations are locked and
// refcounted. when netFD.Close() is called, it calls syscall.Shutdown
// and sets a closing flag. Only when the last reference is removed
// will the fd be closed.

Удача в повторной реализации сети. Хорошие новости в конце всего этого вашего zmq-сокета будут потокобезопасными в go.

Ответ 2

Создайте новый goroutine для каждого fd, который вы хотите подождать, попросите их отправить fd на канал, когда они что-то прочитают, выберите по каналам.

Ответ 3

Доступ ко всем net.Conn в Go можно получить одновременно. Если ваша библиотека основана на этом, проблем не должно быть.

В противном случае, довольно часто в Go, чтобы вызвать один канал на соединение. Этот goroutine часто просто отвечает за чтение из сокета (который может блокировать) и пересылает все данные другому координатору goroutine с использованием канала. Затем этот координатор goroutine может взаимодействовать со всеми этими соединениями сокетов (которые завернуты в канал) и другими событиями, используя оператор select. Также обратите внимание, что вы можете легко добавить буфер к этим каналам, чтобы медленный клиент не мог заблокировать ваш координатор goroutine.