Help: Passing functions through channels

polaris · · 522 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>I was wondering if anyone could help me out. I have the problem posted on SO:</p> <p><a href="http://stackoverflow.com/questions/34648705/go-passing-functions-through-channels">http://stackoverflow.com/questions/34648705/go-passing-functions-through-channels</a></p> <p>Thanks in advance</p> <hr/>**评论:**<br/><br/>singron: <pre><p>I wouldn&#39;t fight the type system here. Go really doesn&#39;t want you to write generics like this, so don&#39;t. I would pass closures over a <code>chan func()</code>. By using a closure, you can capture arbitrary state when you make it and write any number of things to a &#34;return&#34; channel. Anything else you do will have way more <code>interface{}</code> and reflection.</p> <pre><code>package main func main() { funcChan := make(chan func(), 2) resChan := make(chan string, 2) funcChan &lt;- func() { resChan &lt;- &#34;hi&#34; } funcChan &lt;- func() { resChan &lt;- &#34;hello&#34; } go func() { for f := range funcChan { f() } }() println(&lt;-resChan) println(&lt;-resChan) } </code></pre></pre>tyuo9980: <pre><p>the problem is i can&#39;t pass functions with different signatures into chan func()</p></pre>itsamemmario: <pre><p>True, like @singron said, you could make a chan interface{} like in your example, but i don&#39;t recomend it. Do you really have an undefined amount of functions signatures to be passed on the channel? Also why do you want to send functions over the channel in the first place, if it&#39;s just for timing their execution a simpler solution might be better. Range over a slice functions with the same timer for example. I feel as if you are trying to solve the wrong problem: <a href="http://www.codesimplicity.com/post/complexity-and-the-wrong-solution/" rel="nofollow">http://www.codesimplicity.com/post/complexity-and-the-wrong-solution/</a></p> <p>But i might be wrong. I would implement this much the same way as @singron, using closures. They allow you to have one function signature. But by saving the parameters inside the function you can do whatever you want with them.</p> <p>Hope that helps. Ping me if you need some clarification.</p></pre>tyuo9980: <pre><p>So the context is, I&#39;m developing a web app. This web app has a button, when clicked, makes the server call an API ~15 times. But this API only has a limit of 1 call per second. Therefore I need to rate limit my calls.</p> <p>I don&#39;t want to drop the calls, but instead place it in a queue so all the calls will eventually complete. I have NO idea how to do this.</p> <p>What I&#39;m confused about is how I would be able to use channels and a ticker/timer to process concurrent requests.</p> <p>To further elaborate, I then had this idea where I send functions over channels so as I place them in the queue and tick them off one by one, I can send them back to the function that originally created the channel, which had created a block so it would wait for something to return from the channel (&lt;- this is what i really don&#39;t know how to do... the wait for return part with concurrent requests).</p></pre>black-swan-: <pre><blockquote> <p>So the context is, I&#39;m developing a web app. This web app has a button, when clicked, makes the server call an API ~15 times. But this API only has a limit of 1 call per second. Therefore I need to rate limit my calls.</p> <p>I don&#39;t want to drop the calls, but instead place it in a queue so all the calls will eventually complete. I have NO idea how to do this.</p> </blockquote> <p>It sounds like all you need is a semaphore, driven by a ticker.</p> <p><a href="https://play.golang.org/p/8Gus9CWLaL">https://play.golang.org/p/8Gus9CWLaL</a></p> <p>The example assumes the goroutines are already running, but the important part is literally just <code>&lt;-sem</code></p> <p><a href="https://golang.org/doc/effective_go.html#channels">https://golang.org/doc/effective_go.html#channels</a> talks about using channels as semaphores starting at <code>A buffered channel can be used like a semaphore</code></p></pre>tyuo9980: <pre><p>Thanks, I was kinda looking for this, but haven&#39;t tried it yet. I was thinking of doing something like this last night just as I was about to go to bed LOL. #duckdebugging</p></pre>doliner: <pre><p>Maybe this is too stupid... what about:</p> <pre><code>var functions []*func() for _, f := range functions { f() time.Sleep(time.Second) } </code></pre> <p>Channels are for concurrency and it doesn&#39;t seem like you have a concurrency problem here. You don&#39;t want to call the functions concurrently, it seems like the whole point is not to call them concurrently. If you want to make sure that two goros aren&#39;t simultaneously sending request and thus sending more than 1 per second add a global mutex.</p></pre>lightwater: <pre><p>This is one of the few cases for the sync library. Specifically, a mutex.</p> <pre><code>var rateLimiter = sync.Mutex{} func rateLimitedRequest(w http.ResponseWriter, r *http.Request) { rateLimiter.Lock() //make your synchronous rate limited call here go func () { time.Sleep(time.Second) rateLimiter.Unlock() } () //Handle the rest of rateLimitedRequest here. } </code></pre> <p>Or, you could do it with a buffered channel. <a href="https://tour.golang.org/concurrency/3" rel="nofollow">https://tour.golang.org/concurrency/3</a></p> <pre><code>var rateLimiter = make(chan bool, 1) func rateLimitedRequest(w http.ResponseWriter, r *http.Request) { rateLimiter &lt;- true //make your synchronous rate limited call here go func () { time.Sleep(time.Second) &lt;-rateLimiter } () //Handle the rest of rateLimitedRequest here. } </code></pre> <p>The buffered channel approach would allow you to rate limit it to n per-second (whatever your buffer limit is) instead of just 1</p></pre>tyuo9980: <pre><p>Thanks! This made a lot of sense</p></pre>lapingvino: <pre><p>You can encapsulate all functions in functions of type func(). When you send them over the channel, put the function invocation in a closure inside these simple functions.</p></pre>bmurphy1976: <pre><p>Yes you can. Wrap your func(whatever) with a func(). </p></pre>masked82: <pre><p>Alright, here is the codez: <a href="https://play.golang.org/p/XZvb_4BaJF" rel="nofollow">https://play.golang.org/p/XZvb_4BaJF</a></p> <p><em>Notice that it&#39;s not perfect. You have a queue that is executed every second. If the queue is empty and a new item is added, the new item can wait for almost a second before being executed.</em> But this should get you very close to what you need anyway :)</p> <p>This code can be split into 3 section:</p> <ul> <li>The rate limited queue executor, which I call the server (I&#39;m horrible at naming things) - The server doesn&#39;t know anything about the functions. All it does is start a never-ending goroutine that pops the oldest function in the queue, once every second, and calls it. The issue that I talked about above is in this section of the code BTW and I could help you fix it if you want.</li> <li>The Button Click functionality - This shows you how each button click could call 3 diff functions (you could obviously make more/less function calls) using the server and make sure that they are each 1 second apart from each other. You can even add a timeout to any of the functions (to fake latency) and they would still get called 1 second apart. This is the only place that you need channels because you want to make all the function calls as fast as possible (if the first function takes 5 seconds, you only want to wait 1 second to call the second function) and then wait for them to finish so you need to know when they are all done.</li> <li>The Button Click simulation (the main func) - this just shows that 3 button clicks would work as expected. You can also put them in a goroutine to simulate 3 users clicking the button at the same time and it would still work.</li> </ul></pre>tyuo9980: <pre><p>Thanks, all the posts here helped, I think I&#39;m going to use something like this:</p> <p><a href="https://play.golang.org/p/dNzqmD9qjO" rel="nofollow">https://play.golang.org/p/dNzqmD9qjO</a></p></pre>masked82: <pre><p>Oh yea, that looks good. You will probably have to move your background worker for-loop into a goroutine and make it run for ever because your button clicks are going to come after you start your loop. But everything else looks good!</p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

522 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传