本文介绍了使用渠道进行请求 - 响应通信的习惯方式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 也许我只是没有正确阅读规范,或者我的思维模式仍然陷于旧的同步方法中,但Go 发送一种类型以接收其他响应作为响应的正确方式是什么? 我想到的一个方法是: package main 导入fmt 类型请求struct {输出chan字符串参数int } var input = make(chan * request ) var cache = map [int] string {} func processor(){ for { select { case in:=< - input: if result,exists:= cache [in.argument];存在{ in.out< - 结果} 结果:= fmt.Sprintf(%d,in.argument) cache [in.argument] =结果 in.out< - result } } } func main(){ go processor() responseCh:= make(chan string) input< - & request { responseCh, 1,} 结果:=< - responseCh fmt.Println(结果)} 这个例子,但否则它会导致datarace。 这是我应该做的吗?解决方案有很多可能性,取决于您的问题的最佳方法。当你收到某个频道的内容时,没有什么比应用默认的方式 - 你需要自己构建流程(而且你确实在你的问题的例子中做过)。每个请求都发送一个响应频道,给你一个很大的灵活性,因为每个请求都可以选择将响应路由到哪里,但通常不是必需的。 这里有一些其他例子: 1。从同一频道发送和接收 您可以使用无缓冲频道发送和接收响应。这很好地说明了无缓冲的通道实际上是您程序中的同步点。当然限制是我们需要发送与请求和响应完全一样的类型: package main 进口(fmt) func pow2()(c chan int){c = make(chan int) go func(){ for x:= range c {c< - x * x } }() return c } func main(){c:= pow2()c< - 2 fmt.Println(< -c)// = 4 c< ; - 4 fmt.Println(< -c)// = 8 } 。2。发送到一个频道,从另一个频道接收 您可以分开输入和输出频道。如果你愿意,你可以使用缓冲版本。这可以用作请求/响应场景,并且可以让你有一个负责发送请求的路由,另一个负责处理请求,另一个负责接收响应。示例: package main import(fmt) func pow2()(in chan int,out chan int){ in = make(chan int) out = make(chan int) go func (){ for x:= range in { out< - x * x } }() return } func main(){ in,out:= pow2()去func(){ in< - 2 in< - 4 $ (< -out)// = 4 fmt.Println(< -out)// = 8 } 3。发送每个请求的响应频道 这是您在问题中提出的内容。为您提供指定响应路线的灵活性。如果您希望响应按特定处理例程,这非常有用,例如,您有许多客户端需要执行一些任务,并且您希望响应可以由同一客户端接收。 包主要 导入(fmt同步) 类型任务struct {x int c chan int } func pow2(在chan Task中){ for t:= range in { tc } } func main(){ var wg sync.WaitGroup in:= make (chan Task) //两个处理器去掉pow2(进入)去掉pow2(进入) //五个客户端执行一些任务 for n:= 1; n< 5; n ++ { wg.Add(1) go func(x int){ defer wg.Done()c:= make(chan int) in< ; - 任务{x,c} fmt.Printf(%d ** 2 =%d \ n,x,< -c)}(n)} wg.Wait()} 场景不需要用每个任务的返回通道来实现。如果结果具有某种客户端上下文(例如客户端ID),则单个多路复用器可以接收所有响应,然后根据上下文处理它们。 有时通过渠道实现简单的请求 - 响应模式是没有意义的。在设计去程序时,我发现自己试图向系统注入太多频道(仅仅因为我认为它们非常棒)。旧的良好的函数调用有时候是我们所需要的: pre $ code $ package $ main import( fmt) func pow2(x int)int { return x * x } func main(){ fmt.Println(pow2(2)) fmt.Println(pow2(4))} (如果有人遇到类似问题,这可能是一个很好的解决方案。回应您在问题中收到的意见,必须保护单个结构,如缓存, 可能更好地创建一个结构并公开一些方法,这将保护与互斥体的并发使用。) Maybe I'm just not reading the spec right or my mindset is still stuck with older synchronization methods, but what is the right way in Go to send one type as receive something else as a response?One way I had come up with waspackage mainimport "fmt"type request struct { out chan string argument int}var input = make(chan *request)var cache = map[int]string{}func processor() { for { select { case in := <- input: if result, exists := cache[in.argument]; exists { in.out <- result } result := fmt.Sprintf("%d", in.argument) cache[in.argument] = result in.out <- result } }}func main() { go processor() responseCh := make(chan string) input <- &request{ responseCh, 1, } result := <- responseCh fmt.Println(result)}That cache is not really necessary for this example but otherwise it would cause a datarace.Is this what I'm supposed to do? 解决方案 There're plenty of possibilities, depends what is best approach for your problem. When you receive something from a channel, there is nothing like a default way for responding – you need to build the flow by yourself (and you definitely did in the example in your question). Sending a response channel with every request gives you a great flexibility as with every request you can choose where to route the response, but quite often is not necessary.Here are some other examples:1. Sending and receiving from the same channelYou can use unbuffered channel for both sending and receiving the responses. This nicely illustrates that unbuffered channels are in fact a synchronisation points in your program. The limitation is of course that we need to send exactly the same type as request and response:package mainimport ( "fmt")func pow2() (c chan int) { c = make(chan int) go func() { for x := range c { c <- x*x } }() return c}func main() { c := pow2() c <- 2 fmt.Println(<-c) // = 4 c <- 4 fmt.Println(<-c) // = 8}2. Sending to one channel, receiving from anotherYou can separate input and output channels. You would be able to use buffered version if you wish. This can be used as request/response scenario and would allow you to have a route responsible for sending the requests, another one for processing them and yet another for receiving responses. Example:package mainimport ( "fmt")func pow2() (in chan int, out chan int) { in = make(chan int) out = make(chan int) go func() { for x := range in { out <- x*x } }() return}func main() { in, out := pow2() go func() { in <- 2 in <- 4 }() fmt.Println(<-out) // = 4 fmt.Println(<-out) // = 8}3. Sending response channel with every requestThis is what you've presented in the question. Gives you a flexibility of specifying the response route. This is useful if you want the response to hit the specific processing routine, for example you have many clients with some tasks to do and you want the response to be received by the same client.package mainimport ( "fmt" "sync")type Task struct { x int c chan int}func pow2(in chan Task) { for t := range in { t.c <- t.x*t.x } }func main() { var wg sync.WaitGroup in := make(chan Task) // Two processors go pow2(in) go pow2(in) // Five clients with some tasks for n := 1; n < 5; n++ { wg.Add(1) go func(x int) { defer wg.Done() c := make(chan int) in <- Task{x, c} fmt.Printf("%d**2 = %d\n", x, <-c) }(n) } wg.Wait()}Worth saying this scenario doesn't necessary need to be implemented with per-task return channel. If the result has some sort of the client context (for example client id), a single multiplexer could be receiving all the responses and then processing them according to the context.Sometimes it doesn't make sense to involve channels to achieve simple request-response pattern. When designing go programs, I caught myself trying to inject too many channels into the system (just because I think they're really great). Old good function calls is sometimes all we need:package mainimport ( "fmt")func pow2(x int) int { return x*x}func main() { fmt.Println(pow2(2)) fmt.Println(pow2(4))}(And this might be a good solution if anyone encounters similar problem as in your example. Echoing the comments you've received under your question, having to protect a single structure, like cache, it might be better to create a structure and expose some methods, which would protect concurrent use with mutex.) 这篇关于使用渠道进行请求 - 响应通信的习惯方式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
10-20 23:49