在Go语言中,监听事件的惯用方法是什么?

huangapple go评论73阅读模式
英文:

What is an idiomatic method of listening for events in Go?

问题

几个月前,我在思考如何在Go中实现一个可关闭的事件循环,用于一个RPC库。我成功地实现了像这样关闭服务器的功能:

type Server struct {
    listener  net.Listener
    closeChan chan bool
    routines  sync.WaitGroup
}

func (s *Server) Serve() {
    s.routines.Add(1)
    defer s.routines.Done()
    defer s.listener.Close()

    for {
        select {
        case <-s.closeChan:
            // 关闭服务器等等
        default:
            s.listener.SetDeadline(time.Now().Add(2 * time.Second))
            conn, _ := s.listener.Accept()
            // 处理连接的例程
        }
    }
}

func (s *Server) Close() {
    s.closeChan <- true // 信号关闭服务例程
    s.routines.Wait()
}

我发现这种实现的问题是它涉及到一个超时,这意味着关闭时间至少比它应该多2秒。是否有更符合惯用方法的创建事件循环的方法?

英文:

A few months ago I was thinking how to implement a closable event loop in Go, for an RPC library. I managed to facilitate closing the server like so:

type Server struct {
	listener net.Listener
	closeChan chan bool
	routines sync.WaitGroup
}

func (s *Server) Serve() {
	s.routines.Add(1)
	defer s.routines.Done()
	defer s.listener.Close()
	
	for {
		select {
			case &lt;-s.closeChan:
				// close server etc.
			default:
				s.listener.SetDeadline(time.Now().Add(2 * time.Second))
				conn, _ := s.listener.Accept()
				// handle conn routine
		}
	}
}

func (s *Server) Close() {
	s.closeChan &lt;- true // signal to close serve routine
	s.routines.Wait()
}

The problem that I've found with this implementation is it involves a timeout, which means minimum close time is 2 seconds more than it could be. Is there a more idiomatic method of creating an event loop?

答案1

得分: 3

我不认为Go中的事件循环需要循环。

处理关闭和连接似乎更简单的方法是在单独的goroutine中处理:

go func() {
    &lt;-s.closeChan
    // 关闭服务器,释放资源等等
    s.listener.Close()
}()
for {
    conn, err := s.listener.Accept()
    if err != nil {
         // 记录日志,返回
    }
    // 处理连接的例程
}

请注意,您也可以在Close函数中直接关闭监听器,而不使用通道。我在这里所做的是使用Listener.Accept的错误返回值来促进协程间的通信。

如果在关闭和连接处理实现的某个点上,您需要保护一些正在关闭时正在回答的资源,您可以使用互斥锁。但通常可以避免这种情况。

英文:

I don't think that event loops in Go need to be loops.

It would seem simpler to handle closing and connections in separate goroutines:

go func() {
    &lt;-s.closeChan
    // close server, release resources, etc.
    s.listener.Close()
}()
for {
    conn, err := s.listener.Accept()
    if err != nil {
         // log, return
    }
    // handle conn routine
}

Note that you might also close the listener directly in your Close function without using a channel. What I have done here is used the error return value of Listener.Accept to facilitate inter-routine communication.

If at some point of the closing and connection handling implementations you need to protect some resources you're closing while you're answering, you may use a Mutex. But it's generally possible to avoid that.

huangapple
  • 本文由 发表于 2012年11月22日 15:17:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/13507568.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定