【问题标题】:Golang handlefunc with channel带通道的 Golang handlefunc
【发布时间】:2015-02-09 21:37:26
【问题描述】:

我认为这个问题已经被问过(并且可能不止一次),但我找不到它......

我正在学习 Go,我想通过向“处理程序”发送一个通道来扩展经典的 Web 服务器示例。

我有这个标准的东西:

func hello(w http.ResponseWriter, r *http.Request) {
   io.WriteString(w, "Hello world!")
}

func main() {
    http.HandleFunc("/", hello)
    http.ListenAndServe(":8000", nil)
}

现在我希望“hello”函数能够在频道上写东西,供某人消费......我使用“普通”函数所做的方式是创建一个频道:

c := make(chan string) 

并在函数调用中传递 c。比如:

dosomething(c)

但是...如果我想让“hello”访问频道 c,我该怎么做呢?

【问题讨论】:

    标签: go


    【解决方案1】:

    还有其他两种方法可以做到这一点(除了在上一个答案中导出您的频道)。

    第一种是使用一个函数返回另一个处理函数。当函数返回时,它会在通道周围创建一个闭包。

    func makeHello(logger chan string) func(http.ResponseWriter, *http.Request) {
        return func(w http.ResponseWriter, r *http.Request) {
            logger <- r.Host
            io.WriteString(w, "Hello world!")
        }
    }
    

    第二种是使用一个结构体,它将通道作为成员,并使用指针接收器方法来处理请求......

    type DataPasser struct {
        logs chan string
    }
    
    func (p *DataPasser) handleHello(w http.ResponseWriter, r *http.Request) {
        p.logs <- r.URL.String()
        io.WriteString(w, "Hello world")
    }
    

    这是一个完整的工作示例(只需点击 /1 和 /2 即可查看这两个示例)

    package main
    
    import (
        "fmt"
        "io"
        "net/http"
    )
    
    func main() {
        // METHOD 1
        logs := make(chan string)
        go logLogs(logs)
        handleHello := makeHello(logs)
    
        // METHOD 2
        passer := &DataPasser{logs: make(chan string)}
        go passer.log()
    
        http.HandleFunc("/1", handleHello)
        http.HandleFunc("/2", passer.handleHello)
        http.ListenAndServe(":9999", nil)
    }
    
    // METHOD 1
    
    func makeHello(logger chan string) func(http.ResponseWriter, *http.Request) {
        return func(w http.ResponseWriter, r *http.Request) {
            logger <- r.Host
            io.WriteString(w, "Hello world!")
        }
    }
    
    func logLogs(logger chan string) {
        for item := range logger {
            fmt.Println("1. Item", item)
        }
    }
    
    // METHOD 2
    
    type DataPasser struct {
        logs chan string
    }
    
    func (p *DataPasser) handleHello(w http.ResponseWriter, r *http.Request) {
        p.logs <- r.URL.String()
        io.WriteString(w, "Hello world")
    }
    
    func (p *DataPasser) log() {
        for item := range p.logs {
            fmt.Println("2. Item", item)
        }
    }
    

    【讨论】:

    • 非常感谢您提供的出色示例。我最终使用了方法 1,它完美地完成了这项工作。
    • 来自 python 我无法弄清楚如何做这么简单的事情。有趣的是,这个问题实际上有一个非常简单的解决方案,所需要的只是范式转变。
    • 方法 2 不起作用。每次将频道添加到 main() 时,服务器都会启动但不工作。通过使用结构广告接收器或全局 var chan。
    • @Andy 上面的示例代码不起作用吗?我认为它很清楚地表明方法 2 确实有效,但我很想知道我是否错了。
    • 我也用了第一种方法,很有用
    【解决方案2】:

    有几种方法可以解决这个问题,最简单的方法是在一个包中定义一个导出的通道,然后将所述包导入到您想要使用该通道的任何位置。

    package mychannel
    
    var Chan = make(chan string)
    

    【讨论】:

    • 非常感谢您的帮助!最后我改用了sberry提出的方法,但还是很感谢大家的帮助!
    • 这就像“Go-style Singleton Pattern”。
    • @SiuChingPong-AsukaKenji-nah,只是一个全局变量。您可以在 Go 中有一个带有私有类型的单例,该私有类型带有一个公共构造函数,该构造函数返回作为私有类型实例的私有 var,这将强制执行单例模式。不过,我不确定我是否见过任何使用 Go 编写代码的人都这样做过。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-02
    • 1970-01-01
    • 1970-01-01
    • 2017-04-29
    • 2020-07-30
    • 2016-08-30
    相关资源
    最近更新 更多