2012-07-05 5 views
13

나는 Go에서 select {} 구문을 사용하여 여러 채널을 기다릴 수 있으며 syscall.Select() 또는 유사한 기능을 사용하여 여러 파일 설명자를 기다릴 수 있음을 알고 있습니다. 그러나 한 번에 두 채널에서 대기 할 수 있습니까?Go에서 동시에 채널과 파일 설명자를 모두 대기 할 수 있습니까?

배경으로 채널을 통해 메시지를 수신하고 소켓 연결 (gozmq에서 제공됨)을 통해 메시지를 전달하고 동시에 소켓 연결에서 응답을 기다리는 goroutine을 갖고 싶습니다.

기본 라이브러리의 스레드 안전 요구 사항으로 인해 소켓은 한 번에 하나의 스레드에서만 액세스 할 수 있습니다. 따라서 단일 goroutine에서 처리 할 수있는 방법이 있는지 궁금합니다.

+0

호기심을 위해서만 https://github.com/duckie/boson과 같은 다른 프로젝트에서 할 수있는 일입니다. –

답변

10

추상화가 서로 다른 수준에 있기 때문에 채널과 파일 설명자를 모두 선택할 수 없습니다. 채널은 운영 체제에서 go 런타임 및 파일 설명자에 의해 처리됩니다. 당신이 필요로하는 것은 그들 사이에 다리를 만드는 것입니다.이 작업은 net.Pipe()으로 할 수 있습니다.

거의 당신이해야 할 일을 당신의 zmq-소켓과 하나의 "일어나"net.Pipe()을 볼 수 epoll()/select() 하나의 goroutine을 바치고있다. 여론 조사 서버입니다. 다른 goroutine은 읽기 및 쓰기 채널을 수신합니다. 누군가가 읽기 또는 쓰기 채널을 전송하면 두 번째 goroutine이 파이프를 보내어 폴링 서버를 시작합니다.

표준 라이브러리의 넷 패키지가 작동하는 방식입니다. 영감을 얻기 위해 그것을 읽는 것이 좋습니다 (또는 BSD 라이센스가 매우 자유주의입니다).

다음은 pollServer from net 자체에 대한 설명입니다. 이 코드의 의미를 이해하려면 코드를 읽어야하지만, 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's 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's 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's 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-socket의 끝 부분에있는 좋은 소식은 쓰레드가 안전 할 것입니다.

+0

다른 스레드에서'select' 또는'poll'을 실행하면 zmq의 스레드 안전 요구 사항으로 인해 혼란을 일으키지 않을 것입니다. 따라서 goroutine에서이를 수행하고 결과를 채널을 통해 다시 전달하는 것은 좋은 해결책. 감사! –

1

모든 net.Conn의 Go에 동시에 액세스 할 수 있습니다. 라이브러리가이를 기반으로하는 경우 아무런 문제가 없어야합니다.

그렇지 않으면 Go에서 연결 당 하나의 goroutine을 생성하는 것이 일반적입니다. 이 goroutine은 소켓에서 읽는 것을 막고 (차단할 수 있음) 채널을 사용하여 모든 데이터를 다른 코디네이터 goroutine으로 전달합니다. 이 코디네이터 goroutine은 select 문을 사용하여 채널에 래핑 된 모든 소켓 연결 및 다른 이벤트와 상호 작용할 수 있습니다. 또한 해당 채널에 버퍼를 쉽게 추가 할 수 있으므로 느린 클라이언트가 코디네이터의 goroutine을 차단할 수 없습니다.

+0

라이브러리는 net.Conn을 기반으로하지 않습니다. 특히 소켓을 여러 스레드에서 동시에 사용할 수 없다고 말합니다 (http://api.zeromq.org/2-2:zmq-socket). 데이터를 받기 전에 소켓에 ​​쓸 필요가 있기 때문에 소켓에서 읽기를 차단할 수 없습니다. –

2

기다려야 할 fd 각각에 대해 새로운 goroutine을 생성하고 무엇인가를 읽을 때 fd을 채널로 보내도록하고 채널을 선택하십시오.

+0

어떻게 이러한 goroutine을 멈추게 할 것입니까? fd를 닫는 것은 믿을만한 방법이 아닙니다. – chmike

+0

@chmike filedescriptor를 닫아도 스레드가 알림을받지 못하면 OS 팀에 버그를 신고하십시오. IOW, FD를 닫는 것은 문서화되고 신뢰할 수있는 방법으로 프로세스가 알림을 읽도록하는 것입니다. –

+0

읽을 때 차단되면 스레드/프로세스에 알립니다. 그렇지 않으면 그렇지 않습니다. fd가 닫히면 커널은 그것을 재사용 할 수 있습니다. 이 경우 goroutine은 종료를 인식하지 못하고 재사용 된 fd로 식별 된 다른 소스에서 읽기 시작할 수 있습니다. 이러한 이유로 유일한 안정적인 솔루션은 fd에서 select를 사용하고 알림 바이트를 보내는 데 사용되는 파이프를 사용하는 것입니다. – chmike

관련 문제